Merge lp:~rogpeppe/juju-core/312-api-jobs into lp:~juju/juju-core/trunk
- 312-api-jobs
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | William Reade |
Proposed branch: | lp:~rogpeppe/juju-core/312-api-jobs |
Merge into: | lp:~juju/juju-core/trunk |
Prerequisite: | lp:~rogpeppe/juju-core/311-juju-bootstrap-state-change-password-1.5 |
Diff against target: |
1654 lines (+1071/-213) 21 files modified
cmd/jujud/bootstrap.go (+1/-1) cmd/jujud/bootstrap_test.go (+1/-1) cmd/jujud/machine.go (+2/-2) cmd/jujud/machine_test.go (+4/-4) environs/config/config_test.go (+1/-1) environs/dummy/environs.go (+0/-3) juju/testing/repo.go (+0/-1) state/api/params/params.go (+30/-0) state/apiserver/admin.go (+0/-20) state/apiserver/apiserver.go (+750/-0) state/apiserver/common/interfaces.go (+1/-1) state/apiserver/login_test.go (+4/-3) state/apiserver/machine/agent.go (+69/-0) state/apiserver/machine/agent_test.go (+91/-0) state/apiserver/machine/common_test.go (+66/-0) state/apiserver/machine/machiner.go (+12/-12) state/apiserver/machine/machiner_test.go (+9/-61) state/apiserver/resource.go (+0/-82) state/apiserver/root.go (+22/-13) state/machine.go (+6/-6) state/state_test.go (+2/-2) |
To merge this branch: | bzr merge lp:~rogpeppe/juju-core/312-api-jobs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+165705@code.launchpad.net |
Commit message
Description of the change
api: implement Machine.Jobs
Roger Peppe (rogpeppe) wrote : | # |
Roger Peppe (rogpeppe) wrote : | # |
Please take a look.
Roger Peppe (rogpeppe) wrote : | # |
Please take a look.
William Reade (fwereade) wrote : | # |
NOT LGTM. Int jobs aren't great, but they're better than baking bad
names into the API forever, I think.
https:/
File state/machine.go (right):
https:/
state/machine.
// jobNames MUST NEVER BE CHANGED.
...or maybe we could just use ints, as designed originally, and not
lumber the API with guaranteed-
sensible; it conflates jobs with tasks.)
Roger Peppe (rogpeppe) wrote : | # |
Please take a look.
https:/
File state/machine.go (right):
https:/
state/machine.
On 2013/05/27 20:23:20, fwereade wrote:
> // jobNames MUST NEVER BE CHANGED.
> ...or maybe we could just use ints, as designed originally, and not
lumber the
> API with guaranteed-
conflates
> jobs with tasks.)
renamed to JobManageState as agreed in discussion.
William Reade (fwereade) wrote : | # |
LGTM, thanks
Dimiter Naydenov (dimitern) wrote : | # |
Please do not commit this without refactoring it into a facade. We don't
have api/machine.go or apiserver/
- 1242. By Roger Peppe
-
revert all jobs changes
- 1243. By Roger Peppe
-
merge 311-juju-
bootstrap- state-change- password- 1.5 - 1244. By Roger Peppe
-
merge 321-apiserver-
machineagent- api
William Reade (fwereade) wrote : | # |
Unmerged revisions
- 1244. By Roger Peppe
-
merge 321-apiserver-
machineagent- api - 1243. By Roger Peppe
-
merge 311-juju-
bootstrap- state-change- password- 1.5 - 1242. By Roger Peppe
-
revert all jobs changes
- 1241. By Roger Peppe
-
gofmt
- 1240. By Roger Peppe
-
all: rename JobServeAPI to JobManageState
- 1239. By Roger Peppe
-
Merged 311-juju-
bootstrap- state-change- password- 1.5 into 312-api-jobs. - 1238. By Roger Peppe
-
api: implement Machine.Jobs
- 1237. By Roger Peppe
-
merge trunk
- 1236. By Roger Peppe
-
cmd/juju: revert bogus change
- 1235. By Roger Peppe
-
cmd/jujud: change password for machine agent
Preview Diff
1 | === modified file 'cmd/jujud/bootstrap.go' |
2 | --- cmd/jujud/bootstrap.go 2013-06-18 12:56:33 +0000 |
3 | +++ cmd/jujud/bootstrap.go 2013-06-18 12:56:33 +0000 |
4 | @@ -114,7 +114,7 @@ |
5 | // Upgrader, and it's a capability we'll always want to have |
6 | // available for the aforementioned use case. |
7 | jobs := []state.MachineJob{ |
8 | - state.JobManageEnviron, state.JobServeAPI, state.JobHostUnits, |
9 | + state.JobManageEnviron, state.JobManageState, state.JobHostUnits, |
10 | } |
11 | m, err := st.InjectMachine(version.Current.Series, c.Constraints, instanceId, jobs...) |
12 | if err != nil { |
13 | |
14 | === modified file 'cmd/jujud/bootstrap_test.go' |
15 | --- cmd/jujud/bootstrap_test.go 2013-06-18 12:56:33 +0000 |
16 | +++ cmd/jujud/bootstrap_test.go 2013-06-18 12:56:33 +0000 |
17 | @@ -166,7 +166,7 @@ |
18 | m, err := st.Machine("0") |
19 | c.Assert(err, IsNil) |
20 | c.Assert(m.Jobs(), DeepEquals, []state.MachineJob{ |
21 | - state.JobManageEnviron, state.JobServeAPI, state.JobHostUnits, |
22 | + state.JobManageEnviron, state.JobManageState, state.JobHostUnits, |
23 | }) |
24 | } |
25 | |
26 | |
27 | === modified file 'cmd/jujud/machine.go' |
28 | --- cmd/jujud/machine.go 2013-06-11 13:52:19 +0000 |
29 | +++ cmd/jujud/machine.go 2013-06-18 12:56:33 +0000 |
30 | @@ -122,7 +122,7 @@ |
31 | tasks = append(tasks, |
32 | provisioner.NewProvisioner(st, a.MachineId), |
33 | firewaller.NewFirewaller(st)) |
34 | - case state.JobServeAPI: |
35 | + case state.JobManageState: |
36 | // Ignore because it's started independently. |
37 | continue |
38 | default: |
39 | @@ -173,7 +173,7 @@ |
40 | m := entity.(*state.Machine) |
41 | runAPI := false |
42 | for _, job := range m.Jobs() { |
43 | - if job == state.JobServeAPI { |
44 | + if job == state.JobManageState { |
45 | runAPI = true |
46 | break |
47 | } |
48 | |
49 | === modified file 'cmd/jujud/machine_test.go' |
50 | --- cmd/jujud/machine_test.go 2013-06-18 12:56:33 +0000 |
51 | +++ cmd/jujud/machine_test.go 2013-06-18 12:56:33 +0000 |
52 | @@ -110,7 +110,7 @@ |
53 | } |
54 | |
55 | func (s *MachineSuite) TestWithDeadMachine(c *C) { |
56 | - m, _, _ := s.primeAgent(c, state.JobHostUnits, state.JobServeAPI) |
57 | + m, _, _ := s.primeAgent(c, state.JobHostUnits, state.JobManageState) |
58 | err := m.EnsureDead() |
59 | c.Assert(err, IsNil) |
60 | a := s.newAgent(c, m) |
61 | @@ -248,7 +248,7 @@ |
62 | } |
63 | |
64 | func (s *MachineSuite) TestUpgrade(c *C) { |
65 | - m, conf, currentTools := s.primeAgent(c, state.JobServeAPI, state.JobManageEnviron, state.JobHostUnits) |
66 | + m, conf, currentTools := s.primeAgent(c, state.JobManageState, state.JobManageEnviron, state.JobHostUnits) |
67 | addAPIInfo(conf, m) |
68 | err := conf.Write() |
69 | c.Assert(err, IsNil) |
70 | @@ -270,7 +270,7 @@ |
71 | } |
72 | |
73 | func (s *MachineSuite) TestServeAPI(c *C) { |
74 | - stm, conf, _ := s.primeAgent(c, state.JobServeAPI) |
75 | + stm, conf, _ := s.primeAgent(c, state.JobManageState) |
76 | addAPIInfo(conf, stm) |
77 | err := conf.Write() |
78 | c.Assert(err, IsNil) |
79 | @@ -320,7 +320,7 @@ |
80 | }} |
81 | |
82 | func (s *MachineSuite) TestServeAPIWithBadConf(c *C) { |
83 | - m, conf, _ := s.primeAgent(c, state.JobServeAPI) |
84 | + m, conf, _ := s.primeAgent(c, state.JobManageState) |
85 | addAPIInfo(conf, m) |
86 | for i, t := range serveAPIWithBadConfTests { |
87 | c.Logf("test %d: %q", i, t.err) |
88 | |
89 | === modified file 'environs/config/config_test.go' |
90 | --- environs/config/config_test.go 2013-06-18 12:56:33 +0000 |
91 | +++ environs/config/config_test.go 2013-06-10 10:30:13 +0000 |
92 | @@ -130,7 +130,7 @@ |
93 | "name": "my-name", |
94 | "ca-cert": invalidCACert, |
95 | }, |
96 | - err: `bad CA certificate/key in configuration: (asn1:|ASN\.1) syntax error:.*`, |
97 | + err: "bad CA certificate/key in configuration: ASN.1 syntax error:.*", |
98 | }, { |
99 | about: "Invalid CA key", |
100 | attrs: attrs{ |
101 | |
102 | === modified file 'environs/dummy/environs.go' |
103 | --- environs/dummy/environs.go 2013-06-18 12:56:33 +0000 |
104 | +++ environs/dummy/environs.go 2013-06-18 12:56:33 +0000 |
105 | @@ -456,9 +456,6 @@ |
106 | return fmt.Errorf("environment is already bootstrapped") |
107 | } |
108 | if e.ecfg().stateServer() { |
109 | - // TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go |
110 | - // so that we can call it here. |
111 | - |
112 | info := stateInfo() |
113 | st, err := state.Initialize(info, cfg, state.DefaultDialOpts()) |
114 | if err != nil { |
115 | |
116 | === modified file 'juju/testing/repo.go' |
117 | --- juju/testing/repo.go 2013-05-09 21:51:05 +0000 |
118 | +++ juju/testing/repo.go 2013-06-18 12:56:33 +0000 |
119 | @@ -52,7 +52,6 @@ |
120 | c.Assert(ch.URL(), DeepEquals, expectCurl) |
121 | s.AssertCharmUploaded(c, expectCurl) |
122 | units, err := svc.AllUnits() |
123 | - c.Logf("Service units: %+v", units) |
124 | c.Assert(err, IsNil) |
125 | c.Assert(units, HasLen, unitCount) |
126 | s.AssertUnitMachines(c, units) |
127 | |
128 | === renamed file 'state/api/client.go' => 'state/api/apiclient.go' |
129 | === renamed file 'state/api/apiclient.go' => 'state/api/client.go' |
130 | === renamed file 'state/api/apierror.go' => 'state/api/error.go' |
131 | === modified file 'state/api/params/params.go' |
132 | --- state/api/params/params.go 2013-06-18 12:56:33 +0000 |
133 | +++ state/api/params/params.go 2013-06-18 12:56:33 +0000 |
134 | @@ -28,6 +28,12 @@ |
135 | return e.Message |
136 | } |
137 | |
138 | +// GoString implements fmt.GoStringer. It means that a *Error shows its |
139 | +// contents correctly when printed with %#v. |
140 | +func (e Error) GoString() string { |
141 | + return fmt.Sprintf("¶ms.Error{%q, %q}", e.Code, e.Message) |
142 | +} |
143 | + |
144 | // ErrorResults holds the results of calling a bulk operation which |
145 | // mutates multiple entites, like Machiner.SetStatus. The order and |
146 | // number of elements matches the entities specified in the request. |
147 | @@ -87,6 +93,30 @@ |
148 | Machines []MachineLifeResult |
149 | } |
150 | |
151 | +// MachineAgentGetMachinesResults holds the results of a |
152 | +// machineagent.API.GetMachines call. |
153 | +type MachineAgentGetMachinesResults struct { |
154 | + Machines []MachineAgentGetMachinesResult |
155 | +} |
156 | + |
157 | +// MachineJob values define responsibilities that machines may be |
158 | +// expected to fulfil. |
159 | +type MachineJob string |
160 | + |
161 | +const ( |
162 | + JobHostUnits MachineJob = "JobHostUnits" |
163 | + JobManageEnviron MachineJob = "JobManageEnviron" |
164 | + JobManageState MachineJob = "JobManageState" |
165 | +) |
166 | + |
167 | +// MachineAgentGetMachinesResult holds the results of a |
168 | +// machineagent.API.GetMachines call for a single machine. |
169 | +type MachineAgentGetMachinesResult struct { |
170 | + Life Life |
171 | + Jobs []MachineJob |
172 | + Error *Error |
173 | +} |
174 | + |
175 | // ServiceDeploy holds the parameters for making the ServiceDeploy call. |
176 | type ServiceDeploy struct { |
177 | ServiceName string |
178 | |
179 | === removed file 'state/apiserver/admin.go' |
180 | --- state/apiserver/admin.go 2013-05-24 19:03:39 +0000 |
181 | +++ state/apiserver/admin.go 1970-01-01 00:00:00 +0000 |
182 | @@ -1,20 +0,0 @@ |
183 | -// Copyright 2013 Canonical Ltd. |
184 | -// Licensed under the AGPLv3, see LICENCE file for details. |
185 | - |
186 | -package apiserver |
187 | - |
188 | -import "launchpad.net/juju-core/state/api/params" |
189 | - |
190 | -// srvAdmin is the only object that unlogged-in |
191 | -// clients can access. It holds any methods |
192 | -// that are needed to log in. |
193 | -type srvAdmin struct { |
194 | - root *srvRoot |
195 | -} |
196 | - |
197 | -// Login logs in with the provided credentials. |
198 | -// All subsequent requests on the connection will |
199 | -// act as the authenticated user. |
200 | -func (a *srvAdmin) Login(c params.Creds) error { |
201 | - return a.root.user.login(a.root.srv.state, c.AuthTag, c.Password) |
202 | -} |
203 | |
204 | === added file 'state/apiserver/apiserver.go' |
205 | --- state/apiserver/apiserver.go 1970-01-01 00:00:00 +0000 |
206 | +++ state/apiserver/apiserver.go 2013-06-18 12:56:33 +0000 |
207 | @@ -0,0 +1,750 @@ |
208 | +// Copyright 2013 Canonical Ltd. |
209 | +// Licensed under the AGPLv3, see LICENCE file for details. |
210 | + |
211 | +package apiserver |
212 | + |
213 | +import ( |
214 | + "fmt" |
215 | + "launchpad.net/juju-core/charm" |
216 | + "launchpad.net/juju-core/juju" |
217 | + "launchpad.net/juju-core/log" |
218 | + "launchpad.net/juju-core/state" |
219 | + "launchpad.net/juju-core/state/api" |
220 | + "launchpad.net/juju-core/state/api/params" |
221 | + "launchpad.net/juju-core/state/multiwatcher" |
222 | + "launchpad.net/juju-core/state/presence" |
223 | + "launchpad.net/juju-core/state/statecmd" |
224 | + statewatcher "launchpad.net/juju-core/state/watcher" |
225 | + "strconv" |
226 | + "sync" |
227 | +) |
228 | + |
229 | +// srvRoot represents a single client's connection to the state. |
230 | +type srvRoot struct { |
231 | + admin *srvAdmin |
232 | + client *srvClient |
233 | + srv *Server |
234 | + resources *resources |
235 | + |
236 | + user authUser |
237 | +} |
238 | + |
239 | +// srvAdmin is the only object that unlogged-in |
240 | +// clients can access. It holds any methods |
241 | +// that are needed to log in. |
242 | +type srvAdmin struct { |
243 | + root *srvRoot |
244 | +} |
245 | + |
246 | +// srvMachine serves API methods on a machine. |
247 | +type srvMachine struct { |
248 | + root *srvRoot |
249 | + m *state.Machine |
250 | +} |
251 | + |
252 | +// srvUnit serves API methods on a unit. |
253 | +type srvUnit struct { |
254 | + root *srvRoot |
255 | + u *state.Unit |
256 | +} |
257 | + |
258 | +// srvUser serves API methods on a state User. |
259 | +type srvUser struct { |
260 | + root *srvRoot |
261 | + u *state.User |
262 | +} |
263 | + |
264 | +// srvClient serves client-specific API methods. |
265 | +type srvClient struct { |
266 | + root *srvRoot |
267 | +} |
268 | + |
269 | +func newStateServer(srv *Server) *srvRoot { |
270 | + r := &srvRoot{ |
271 | + srv: srv, |
272 | + resources: newResources(), |
273 | + } |
274 | + r.admin = &srvAdmin{ |
275 | + root: r, |
276 | + } |
277 | + r.client = &srvClient{ |
278 | + root: r, |
279 | + } |
280 | + return r |
281 | +} |
282 | + |
283 | +// Kill implements rpc.Killer. It cleans up any resources that need |
284 | +// cleaning up to ensure that all outstanding requests return. |
285 | +func (r *srvRoot) Kill() { |
286 | + r.resources.stopAll() |
287 | +} |
288 | + |
289 | +// Admin returns an object that provides API access |
290 | +// to methods that can be called even when not |
291 | +// authenticated. |
292 | +func (r *srvRoot) Admin(id string) (*srvAdmin, error) { |
293 | + if id != "" { |
294 | + // Safeguard id for possible future use. |
295 | + return nil, errBadId |
296 | + } |
297 | + return r.admin, nil |
298 | +} |
299 | + |
300 | +// requireAgent checks whether the current client is an agent and hence |
301 | +// may access the agent APIs. We filter out non-agents when calling one |
302 | +// of the accessor functions (Machine, Unit, etc) which avoids us making |
303 | +// the check in every single request method. |
304 | +func (r *srvRoot) requireAgent() error { |
305 | + e := r.user.authenticator() |
306 | + if e == nil { |
307 | + return errNotLoggedIn |
308 | + } |
309 | + if !isAgent(e) { |
310 | + return errPerm |
311 | + } |
312 | + return nil |
313 | +} |
314 | + |
315 | +// requireClient returns an error unless the current |
316 | +// client is a juju client user. |
317 | +func (r *srvRoot) requireClient() error { |
318 | + e := r.user.authenticator() |
319 | + if e == nil { |
320 | + return errNotLoggedIn |
321 | + } |
322 | + if isAgent(e) { |
323 | + return errPerm |
324 | + } |
325 | + return nil |
326 | +} |
327 | + |
328 | +// Machine returns an object that provides |
329 | +// API access to methods on a state.Machine. |
330 | +func (r *srvRoot) Machine(id string) (*srvMachine, error) { |
331 | + if err := r.requireAgent(); err != nil { |
332 | + return nil, err |
333 | + } |
334 | + m, err := r.srv.state.Machine(id) |
335 | + if err != nil { |
336 | + return nil, err |
337 | + } |
338 | + return &srvMachine{ |
339 | + root: r, |
340 | + m: m, |
341 | + }, nil |
342 | +} |
343 | + |
344 | +// Unit returns an object that provides |
345 | +// API access to methods on a state.Unit. |
346 | +func (r *srvRoot) Unit(name string) (*srvUnit, error) { |
347 | + if err := r.requireAgent(); err != nil { |
348 | + return nil, err |
349 | + } |
350 | + u, err := r.srv.state.Unit(name) |
351 | + if err != nil { |
352 | + return nil, err |
353 | + } |
354 | + return &srvUnit{ |
355 | + root: r, |
356 | + u: u, |
357 | + }, nil |
358 | +} |
359 | + |
360 | +// User returns an object that provides |
361 | +// API access to methods on a state.User. |
362 | +func (r *srvRoot) User(name string) (*srvUser, error) { |
363 | + // Any user is allowed to access their own user object. |
364 | + // We check at this level rather than at the operation |
365 | + // level to stop malicious probing for current user names. |
366 | + // When we provide support for user administration, |
367 | + // this will need to be changed to allow access to |
368 | + // the administrator. |
369 | + e := r.user.authenticator() |
370 | + if e == nil { |
371 | + return nil, errNotLoggedIn |
372 | + } |
373 | + if e.Tag() != name { |
374 | + return nil, errPerm |
375 | + } |
376 | + u, err := r.srv.state.User(name) |
377 | + if err != nil { |
378 | + return nil, err |
379 | + } |
380 | + return &srvUser{ |
381 | + root: r, |
382 | + u: u, |
383 | + }, nil |
384 | +} |
385 | + |
386 | +// Pinger returns an object that provides API access to methods on a |
387 | +// presence.Pinger. Each client has its own current set of pingers, |
388 | +// stored in r.resources. |
389 | +func (r *srvRoot) Pinger(id string) (*srvResource, error) { |
390 | + if err := r.requireAgent(); err != nil { |
391 | + return nil, err |
392 | + } |
393 | + pinger := r.resources.get(id) |
394 | + if pinger == nil { |
395 | + return nil, errUnknownPinger |
396 | + } |
397 | + if _, ok := pinger.resource.(*presence.Pinger); !ok { |
398 | + return nil, errUnknownPinger |
399 | + } |
400 | + return pinger, nil |
401 | +} |
402 | + |
403 | +// EntityWatcher returns an object that provides |
404 | +// API access to methods on a state.EntityWatcher. |
405 | +// Each client has its own current set of watchers, stored |
406 | +// in r.resources. |
407 | +func (r *srvRoot) EntityWatcher(id string) (srvEntityWatcher, error) { |
408 | + if err := r.requireAgent(); err != nil { |
409 | + return srvEntityWatcher{}, err |
410 | + } |
411 | + watcher := r.resources.get(id) |
412 | + if watcher == nil { |
413 | + return srvEntityWatcher{}, errUnknownWatcher |
414 | + } |
415 | + if _, ok := watcher.resource.(*state.EntityWatcher); !ok { |
416 | + return srvEntityWatcher{}, errUnknownWatcher |
417 | + } |
418 | + return srvEntityWatcher{watcher}, nil |
419 | +} |
420 | + |
421 | +// AllWatcher returns an object that provides API access to methods on |
422 | +// a state/multiwatcher.Watcher, which watches any changes to the |
423 | +// state. Each client has its own current set of watchers, stored in |
424 | +// r.resources. |
425 | +func (r *srvRoot) AllWatcher(id string) (srvClientAllWatcher, error) { |
426 | + if err := r.requireClient(); err != nil { |
427 | + return srvClientAllWatcher{}, err |
428 | + } |
429 | + watcher := r.resources.get(id) |
430 | + if watcher == nil { |
431 | + return srvClientAllWatcher{}, errUnknownWatcher |
432 | + } |
433 | + if _, ok := watcher.resource.(*multiwatcher.Watcher); !ok { |
434 | + return srvClientAllWatcher{}, errUnknownWatcher |
435 | + } |
436 | + return srvClientAllWatcher{watcher}, nil |
437 | + |
438 | +} |
439 | + |
440 | +// Client returns an object that provides access |
441 | +// to methods accessible to non-agent clients. |
442 | +func (r *srvRoot) Client(id string) (*srvClient, error) { |
443 | + if err := r.requireClient(); err != nil { |
444 | + return nil, err |
445 | + } |
446 | + if id != "" { |
447 | + // Safeguard id for possible future use. |
448 | + return nil, errBadId |
449 | + } |
450 | + return r.client, nil |
451 | +} |
452 | + |
453 | +type tagger interface { |
454 | + Tag() string |
455 | +} |
456 | + |
457 | +// authOwner returns whether the authenticated user's tag matches the |
458 | +// given entity's tag. |
459 | +func (r *srvRoot) authOwner(entity tagger) bool { |
460 | + authUser := r.user.authenticator() |
461 | + return authUser.Tag() == entity.Tag() |
462 | +} |
463 | + |
464 | +// authEnvironManager returns whether the authenticated user is a |
465 | +// machine with running the ManageEnviron job. |
466 | +func (r *srvRoot) authEnvironManager() bool { |
467 | + authUser := r.user.authenticator() |
468 | + return isMachineWithJob(authUser, state.JobManageEnviron) |
469 | +} |
470 | + |
471 | +type srvEntityWatcher struct { |
472 | + *srvResource |
473 | +} |
474 | + |
475 | +// Next returns when a change has occurred to the |
476 | +// entity being watched since the most recent call to Next |
477 | +// or the Watch call that created the EntityWatcher. |
478 | +func (w srvEntityWatcher) Next() error { |
479 | + watcher := w.resource.(*state.EntityWatcher) |
480 | + if _, ok := <-watcher.Changes(); ok { |
481 | + return nil |
482 | + } |
483 | + err := watcher.Err() |
484 | + if err == nil { |
485 | + err = errStoppedWatcher |
486 | + } |
487 | + return err |
488 | +} |
489 | + |
490 | +func (c *srvClient) Status() (api.Status, error) { |
491 | + ms, err := c.root.srv.state.AllMachines() |
492 | + if err != nil { |
493 | + return api.Status{}, err |
494 | + } |
495 | + status := api.Status{ |
496 | + Machines: make(map[string]api.MachineInfo), |
497 | + } |
498 | + for _, m := range ms { |
499 | + instId, _ := m.InstanceId() |
500 | + status.Machines[m.Id()] = api.MachineInfo{ |
501 | + InstanceId: string(instId), |
502 | + } |
503 | + } |
504 | + return status, nil |
505 | +} |
506 | + |
507 | +func (c *srvClient) WatchAll() (params.AllWatcherId, error) { |
508 | + w := c.root.srv.state.Watch() |
509 | + return params.AllWatcherId{ |
510 | + AllWatcherId: c.root.resources.register(w).id, |
511 | + }, nil |
512 | +} |
513 | + |
514 | +type srvClientAllWatcher struct { |
515 | + *srvResource |
516 | +} |
517 | + |
518 | +func (aw srvClientAllWatcher) Next() (params.AllWatcherNextResults, error) { |
519 | + deltas, err := aw.resource.(*multiwatcher.Watcher).Next() |
520 | + return params.AllWatcherNextResults{ |
521 | + Deltas: deltas, |
522 | + }, err |
523 | +} |
524 | + |
525 | +func (aw srvClientAllWatcher) Stop() error { |
526 | + return aw.resource.(*multiwatcher.Watcher).Stop() |
527 | +} |
528 | + |
529 | +// ServiceSet implements the server side of Client.ServerSet. |
530 | +func (c *srvClient) ServiceSet(p params.ServiceSet) error { |
531 | + svc, err := c.root.srv.state.Service(p.ServiceName) |
532 | + if err != nil { |
533 | + return err |
534 | + } |
535 | + return svc.SetConfig(p.Options) |
536 | +} |
537 | + |
538 | +// ServiceSetYAML implements the server side of Client.ServerSetYAML. |
539 | +func (c *srvClient) ServiceSetYAML(p params.ServiceSetYAML) error { |
540 | + svc, err := c.root.srv.state.Service(p.ServiceName) |
541 | + if err != nil { |
542 | + return err |
543 | + } |
544 | + return svc.SetConfigYAML([]byte(p.Config)) |
545 | +} |
546 | + |
547 | +// ServiceGet returns the configuration for a service. |
548 | +func (c *srvClient) ServiceGet(args params.ServiceGet) (params.ServiceGetResults, error) { |
549 | + return statecmd.ServiceGet(c.root.srv.state, args) |
550 | +} |
551 | + |
552 | +// Resolved implements the server side of Client.Resolved. |
553 | +func (c *srvClient) Resolved(p params.Resolved) error { |
554 | + unit, err := c.root.srv.state.Unit(p.UnitName) |
555 | + if err != nil { |
556 | + return err |
557 | + } |
558 | + return unit.Resolve(p.Retry) |
559 | +} |
560 | + |
561 | +// ServiceExpose changes the juju-managed firewall to expose any ports that |
562 | +// were also explicitly marked by units as open. |
563 | +func (c *srvClient) ServiceExpose(args params.ServiceExpose) error { |
564 | + return statecmd.ServiceExpose(c.root.srv.state, args) |
565 | +} |
566 | + |
567 | +// ServiceUnexpose changes the juju-managed firewall to unexpose any ports that |
568 | +// were also explicitly marked by units as open. |
569 | +func (c *srvClient) ServiceUnexpose(args params.ServiceUnexpose) error { |
570 | + return statecmd.ServiceUnexpose(c.root.srv.state, args) |
571 | +} |
572 | + |
573 | +var CharmStore charm.Repository = charm.Store |
574 | + |
575 | +// ServiceDeploy fetches the charm from the charm store and deploys it. Local |
576 | +// charms are not supported. |
577 | +func (c *srvClient) ServiceDeploy(args params.ServiceDeploy) error { |
578 | + state := c.root.srv.state |
579 | + conf, err := state.EnvironConfig() |
580 | + if err != nil { |
581 | + return err |
582 | + } |
583 | + curl, err := charm.InferURL(args.CharmUrl, conf.DefaultSeries()) |
584 | + if err != nil { |
585 | + return err |
586 | + } |
587 | + conn, err := juju.NewConnFromState(state) |
588 | + if err != nil { |
589 | + return err |
590 | + } |
591 | + if args.NumUnits == 0 { |
592 | + args.NumUnits = 1 |
593 | + } |
594 | + charm, err := conn.PutCharm(curl, CharmStore, false) |
595 | + if err != nil { |
596 | + return err |
597 | + } |
598 | + serviceName := args.ServiceName |
599 | + if serviceName == "" { |
600 | + serviceName = curl.Name |
601 | + } |
602 | + deployArgs := juju.DeployServiceParams{ |
603 | + Charm: charm, |
604 | + ServiceName: serviceName, |
605 | + NumUnits: args.NumUnits, |
606 | + // BUG(lp:1162122): Config/ConfigYAML have no tests. |
607 | + Config: args.Config, |
608 | + ConfigYAML: args.ConfigYAML, |
609 | + Constraints: args.Constraints, |
610 | + } |
611 | + _, err = conn.DeployService(deployArgs) |
612 | + return err |
613 | +} |
614 | + |
615 | +// AddServiceUnits adds a given number of units to a service. |
616 | +func (c *srvClient) AddServiceUnits(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) { |
617 | + units, err := statecmd.AddServiceUnits(c.root.srv.state, args) |
618 | + if err != nil { |
619 | + return params.AddServiceUnitsResults{}, err |
620 | + } |
621 | + unitNames := make([]string, len(units)) |
622 | + for i, unit := range units { |
623 | + unitNames[i] = unit.String() |
624 | + } |
625 | + return params.AddServiceUnitsResults{Units: unitNames}, nil |
626 | +} |
627 | + |
628 | +// DestroyServiceUnits removes a given set of service units. |
629 | +func (c *srvClient) DestroyServiceUnits(args params.DestroyServiceUnits) error { |
630 | + return statecmd.DestroyServiceUnits(c.root.srv.state, args) |
631 | +} |
632 | + |
633 | +// ServiceDestroy destroys a given service. |
634 | +func (c *srvClient) ServiceDestroy(args params.ServiceDestroy) error { |
635 | + return statecmd.ServiceDestroy(c.root.srv.state, args) |
636 | +} |
637 | + |
638 | +// GetServiceConstraints returns the constraints for a given service. |
639 | +func (c *srvClient) GetServiceConstraints(args params.GetServiceConstraints) (params.GetServiceConstraintsResults, error) { |
640 | + return statecmd.GetServiceConstraints(c.root.srv.state, args) |
641 | +} |
642 | + |
643 | +// SetServiceConstraints sets the constraints for a given service. |
644 | +func (c *srvClient) SetServiceConstraints(args params.SetServiceConstraints) error { |
645 | + return statecmd.SetServiceConstraints(c.root.srv.state, args) |
646 | +} |
647 | + |
648 | +// AddRelation adds a relation between the specified endpoints and returns the relation info. |
649 | +func (c *srvClient) AddRelation(args params.AddRelation) (params.AddRelationResults, error) { |
650 | + return statecmd.AddRelation(c.root.srv.state, args) |
651 | +} |
652 | + |
653 | +// DestroyRelation removes the relation between the specified endpoints. |
654 | +func (c *srvClient) DestroyRelation(args params.DestroyRelation) error { |
655 | + return statecmd.DestroyRelation(c.root.srv.state, args) |
656 | +} |
657 | + |
658 | +// CharmInfo returns information about the requested charm. |
659 | +func (c *srvClient) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) { |
660 | + curl, err := charm.ParseURL(args.CharmURL) |
661 | + if err != nil { |
662 | + return api.CharmInfo{}, err |
663 | + } |
664 | + charm, err := c.root.srv.state.Charm(curl) |
665 | + if err != nil { |
666 | + return api.CharmInfo{}, err |
667 | + } |
668 | + info := api.CharmInfo{ |
669 | + Revision: charm.Revision(), |
670 | + URL: curl.String(), |
671 | + Config: charm.Config(), |
672 | + Meta: charm.Meta(), |
673 | + } |
674 | + return info, nil |
675 | +} |
676 | + |
677 | +// EnvironmentInfo returns information about the current environment (default |
678 | +// series and type). |
679 | +func (c *srvClient) EnvironmentInfo() (api.EnvironmentInfo, error) { |
680 | + conf, err := c.root.srv.state.EnvironConfig() |
681 | + if err != nil { |
682 | + return api.EnvironmentInfo{}, err |
683 | + } |
684 | + info := api.EnvironmentInfo{ |
685 | + DefaultSeries: conf.DefaultSeries(), |
686 | + ProviderType: conf.Type(), |
687 | + Name: conf.Name(), |
688 | + } |
689 | + return info, nil |
690 | +} |
691 | + |
692 | +// GetAnnotations returns annotations about a given entity. |
693 | +func (c *srvClient) GetAnnotations(args params.GetAnnotations) (params.GetAnnotationsResults, error) { |
694 | + entity, err := c.root.srv.state.Annotator(args.Tag) |
695 | + if err != nil { |
696 | + return params.GetAnnotationsResults{}, err |
697 | + } |
698 | + ann, err := entity.Annotations() |
699 | + if err != nil { |
700 | + return params.GetAnnotationsResults{}, err |
701 | + } |
702 | + return params.GetAnnotationsResults{Annotations: ann}, nil |
703 | +} |
704 | + |
705 | +// SetAnnotations stores annotations about a given entity. |
706 | +func (c *srvClient) SetAnnotations(args params.SetAnnotations) error { |
707 | + entity, err := c.root.srv.state.Annotator(args.Tag) |
708 | + if err != nil { |
709 | + return err |
710 | + } |
711 | + return entity.SetAnnotations(args.Pairs) |
712 | +} |
713 | + |
714 | +// Login logs in with the provided credentials. |
715 | +// All subsequent requests on the connection will |
716 | +// act as the authenticated user. |
717 | +func (a *srvAdmin) Login(c params.Creds) error { |
718 | + return a.root.user.login(a.root.srv.state, c.AuthTag, c.Password) |
719 | +} |
720 | + |
721 | +// Get retrieves all the details of a machine. |
722 | +func (m *srvMachine) Get() (info params.Machine) { |
723 | + instId, _ := m.m.InstanceId() |
724 | + info.InstanceId = string(instId) |
725 | + info.Life = params.Life(m.m.Life().String()) |
726 | + return |
727 | +} |
728 | + |
729 | +func (m *srvMachine) Watch() (params.EntityWatcherId, error) { |
730 | + w := m.m.Watch() |
731 | + if _, ok := <-w.Changes(); !ok { |
732 | + return params.EntityWatcherId{}, statewatcher.MustErr(w) |
733 | + } |
734 | + return params.EntityWatcherId{ |
735 | + EntityWatcherId: m.root.resources.register(w).id, |
736 | + }, nil |
737 | +} |
738 | + |
739 | +// SetAgentAlive signals that the agent for machine m is alive. |
740 | +func (m *srvMachine) SetAgentAlive() (params.PingerId, error) { |
741 | + if !m.root.authOwner(m.m) { |
742 | + return params.PingerId{}, errPerm |
743 | + } |
744 | + pinger, err := m.m.SetAgentAlive() |
745 | + if err != nil { |
746 | + return params.PingerId{}, err |
747 | + } |
748 | + return params.PingerId{ |
749 | + PingerId: m.root.resources.register(pinger).id, |
750 | + }, nil |
751 | +} |
752 | + |
753 | +// EnsureDead sets the machine lifecycle to Dead if it is Alive or Dying. |
754 | +// It does nothing otherwise. See machine.EnsureDead(). |
755 | +func (m *srvMachine) EnsureDead() error { |
756 | + if !m.root.authOwner(m.m) { |
757 | + return errPerm |
758 | + } |
759 | + return m.m.EnsureDead() |
760 | +} |
761 | + |
762 | +// SetStatus sets the status of the machine. |
763 | +func (m *srvMachine) SetStatus(status params.SetStatus) error { |
764 | + if !m.root.authOwner(m.m) && !m.root.authEnvironManager() { |
765 | + return errPerm |
766 | + } |
767 | + return m.m.SetStatus(status.Status, status.Info) |
768 | +} |
769 | + |
770 | +func setPassword(e state.TaggedAuthenticator, password string) error { |
771 | + // Catch expected common case of mispelled |
772 | + // or missing Password parameter. |
773 | + if password == "" { |
774 | + return fmt.Errorf("password is empty") |
775 | + } |
776 | + return e.SetPassword(password) |
777 | +} |
778 | + |
779 | +// SetPassword sets the machine's password. |
780 | +func (m *srvMachine) SetPassword(p params.Password) error { |
781 | + if !m.root.authOwner(m.m) && !m.root.authEnvironManager() { |
782 | + return errPerm |
783 | + } |
784 | + if err := setPassword(m.m, p.Password); err != nil { |
785 | + return err |
786 | + } |
787 | + // Grant access to the mongo state if the machine requires it. |
788 | + if isMachineWithJob(m.m, state.JobManageEnviron) || |
789 | + isMachineWithJob(m.m, state.JobServeAPI) { |
790 | + return m.m.SetMongoPassword(p.Password) |
791 | + } |
792 | + return nil |
793 | +} |
794 | + |
795 | +// Get retrieves all the details of a unit. |
796 | +func (u *srvUnit) Get() (params.Unit, error) { |
797 | + var ru params.Unit |
798 | + ru.DeployerTag, _ = u.u.DeployerTag() |
799 | + // TODO add other unit attributes |
800 | + return ru, nil |
801 | +} |
802 | + |
803 | +// SetPassword sets the unit's password. |
804 | +func (u *srvUnit) SetPassword(p params.Password) error { |
805 | + tag := u.root.user.authenticator().Tag() |
806 | + // Allow: |
807 | + // - the unit itself. |
808 | + // - the machine responsible for unit, if unit is principal |
809 | + // - the unit's principal unit, if unit is subordinate |
810 | + allow := tag == u.u.Tag() |
811 | + if !allow { |
812 | + deployerTag, ok := u.u.DeployerTag() |
813 | + allow = ok && tag == deployerTag |
814 | + } |
815 | + if !allow { |
816 | + return errPerm |
817 | + } |
818 | + return setPassword(u.u, p.Password) |
819 | +} |
820 | + |
821 | +// SetPassword sets the user's password. |
822 | +func (u *srvUser) SetPassword(p params.Password) error { |
823 | + return setPassword(u.u, p.Password) |
824 | +} |
825 | + |
826 | +// Get retrieves all details of a user. |
827 | +func (u *srvUser) Get() (params.User, error) { |
828 | + return params.User{}, nil |
829 | +} |
830 | + |
831 | +// authUser holds login details. It's ok to call |
832 | +// its methods concurrently. |
833 | +type authUser struct { |
834 | + mu sync.Mutex |
835 | + entity state.TaggedAuthenticator // logged-in entity (access only when mu is locked) |
836 | +} |
837 | + |
838 | +// login authenticates as entity with the given name,. |
839 | +func (u *authUser) login(st *state.State, tag, password string) error { |
840 | + u.mu.Lock() |
841 | + defer u.mu.Unlock() |
842 | + entity, err := st.Authenticator(tag) |
843 | + if err != nil && !state.IsNotFound(err) { |
844 | + return err |
845 | + } |
846 | + // We return the same error when an entity |
847 | + // does not exist as for a bad password, so that |
848 | + // we don't allow unauthenticated users to find information |
849 | + // about existing entities. |
850 | + if err != nil || !entity.PasswordValid(password) { |
851 | + return errBadCreds |
852 | + } |
853 | + u.entity = entity |
854 | + return nil |
855 | +} |
856 | + |
857 | +// authenticator returns the currently logged-in authenticator entity, or nil |
858 | +// if not currently logged on. The returned entity should not be modified |
859 | +// because it may be used concurrently. |
860 | +func (u *authUser) authenticator() state.TaggedAuthenticator { |
861 | + u.mu.Lock() |
862 | + defer u.mu.Unlock() |
863 | + return u.entity |
864 | +} |
865 | + |
866 | +// isMachineWithJob returns whether the given entity is a machine that |
867 | +// is configured to run the given job. |
868 | +func isMachineWithJob(e state.TaggedAuthenticator, j state.MachineJob) bool { |
869 | + m, ok := e.(*state.Machine) |
870 | + if !ok { |
871 | + return false |
872 | + } |
873 | + for _, mj := range m.Jobs() { |
874 | + if mj == j { |
875 | + return true |
876 | + } |
877 | + } |
878 | + return false |
879 | +} |
880 | + |
881 | +// isAgent returns whether the given entity is an agent. |
882 | +func isAgent(e state.TaggedAuthenticator) bool { |
883 | + _, isUser := e.(*state.User) |
884 | + return !isUser |
885 | +} |
886 | + |
887 | +// resource represents the interface provided by state watchers and pingers. |
888 | +type resource interface { |
889 | + Stop() error |
890 | +} |
891 | + |
892 | +// resources holds all the resources for a connection. |
893 | +type resources struct { |
894 | + mu sync.Mutex |
895 | + maxId uint64 |
896 | + rs map[string]*srvResource |
897 | +} |
898 | + |
899 | +// srvResource holds the details of a resource. It also implements the |
900 | +// Stop RPC method for all resources. |
901 | +type srvResource struct { |
902 | + rs *resources |
903 | + resource resource |
904 | + id string |
905 | +} |
906 | + |
907 | +// Stop stops the given resource. It causes any outstanding |
908 | +// Next calls to return a CodeStopped error. |
909 | +// Any subsequent Next calls will return a CodeNotFound |
910 | +// error because the resource will no longer exist. |
911 | +func (r *srvResource) Stop() error { |
912 | + err := r.resource.Stop() |
913 | + r.rs.mu.Lock() |
914 | + defer r.rs.mu.Unlock() |
915 | + delete(r.rs.rs, r.id) |
916 | + return err |
917 | +} |
918 | + |
919 | +func newResources() *resources { |
920 | + return &resources{ |
921 | + rs: make(map[string]*srvResource), |
922 | + } |
923 | +} |
924 | + |
925 | +// get returns the srvResource registered with the given |
926 | +// id, or nil if there is no such resource. |
927 | +func (rs *resources) get(id string) *srvResource { |
928 | + rs.mu.Lock() |
929 | + defer rs.mu.Unlock() |
930 | + return rs.rs[id] |
931 | +} |
932 | + |
933 | +// register records the given watcher and returns |
934 | +// a srvResource instance for it. |
935 | +func (rs *resources) register(r resource) *srvResource { |
936 | + rs.mu.Lock() |
937 | + defer rs.mu.Unlock() |
938 | + rs.maxId++ |
939 | + sr := &srvResource{ |
940 | + rs: rs, |
941 | + id: strconv.FormatUint(rs.maxId, 10), |
942 | + resource: r, |
943 | + } |
944 | + rs.rs[sr.id] = sr |
945 | + return sr |
946 | +} |
947 | + |
948 | +func (rs *resources) stopAll() { |
949 | + rs.mu.Lock() |
950 | + defer rs.mu.Unlock() |
951 | + for _, r := range rs.rs { |
952 | + if err := r.resource.Stop(); err != nil { |
953 | + log.Errorf("state/api: error stopping %T resource: %v", r, err) |
954 | + } |
955 | + } |
956 | + rs.rs = make(map[string]*srvResource) |
957 | +} |
958 | |
959 | === added file 'state/apiserver/apiserver.test' |
960 | Binary files state/apiserver/apiserver.test 1970-01-01 00:00:00 +0000 and state/apiserver/apiserver.test 2013-06-18 12:56:33 +0000 differ |
961 | === modified file 'state/apiserver/common/interfaces.go' |
962 | --- state/apiserver/common/interfaces.go 2013-06-06 17:09:49 +0000 |
963 | +++ state/apiserver/common/interfaces.go 2013-06-18 12:56:33 +0000 |
964 | @@ -25,7 +25,7 @@ |
965 | |
966 | // AuthOwner returns whether the authenticated entity is the same |
967 | // as the given entity. |
968 | - AuthOwner(entity Tagger) bool |
969 | + AuthOwner(tag string) bool |
970 | |
971 | // AuthEnvironManager returns whether the authenticated entity is |
972 | // a machine running the environment manager job. |
973 | |
974 | === modified file 'state/apiserver/login_test.go' |
975 | --- state/apiserver/login_test.go 2013-06-06 17:55:14 +0000 |
976 | +++ state/apiserver/login_test.go 2013-06-18 12:56:33 +0000 |
977 | @@ -61,6 +61,10 @@ |
978 | } |
979 | for i, t := range badLoginTests { |
980 | c.Logf("test %d; entity %q; password %q", i, t.tag, t.password) |
981 | + // Note that Open does not log in if the tag and password |
982 | + // are empty. This allows us to test operations on the connection |
983 | + // before calling Login, which we could not do if Open |
984 | + // always logged in. |
985 | info.Tag = "" |
986 | info.Password = "" |
987 | func() { |
988 | @@ -72,9 +76,6 @@ |
989 | c.Assert(err, ErrorMatches, "not logged in") |
990 | c.Assert(api.ErrCode(err), Equals, api.CodeUnauthorized, Commentf("error %#v", err)) |
991 | |
992 | - // TODO (dimitern) This a really awkward way of testing - |
993 | - // calling Login again here to reauth on the same |
994 | - // connection. Seems wrong. |
995 | err = st.Login(t.tag, t.password) |
996 | c.Assert(err, ErrorMatches, t.err) |
997 | c.Assert(api.ErrCode(err), Equals, t.code) |
998 | |
999 | === renamed directory 'state/apiserver/machiner' => 'state/apiserver/machine' |
1000 | === added file 'state/apiserver/machine/agent.go' |
1001 | --- state/apiserver/machine/agent.go 1970-01-01 00:00:00 +0000 |
1002 | +++ state/apiserver/machine/agent.go 2013-06-18 12:56:33 +0000 |
1003 | @@ -0,0 +1,69 @@ |
1004 | +// Copyright 2013 Canonical Ltd. |
1005 | +// Licensed under the AGPLv3, see LICENCE file for details. |
1006 | + |
1007 | +// The machine package implements the API interfaces |
1008 | +// used by the machine agent. |
1009 | +package machine |
1010 | + |
1011 | +import ( |
1012 | + "launchpad.net/juju-core/state" |
1013 | + "launchpad.net/juju-core/state/api/params" |
1014 | + "launchpad.net/juju-core/state/apiserver/common" |
1015 | +) |
1016 | + |
1017 | +type AgentAPI struct { |
1018 | + st *state.State |
1019 | + auth common.Authorizer |
1020 | +} |
1021 | + |
1022 | +// NewAgentAPI returns an object implementing the machine agent API |
1023 | +// for the given |
1024 | +func NewAgentAPI(st *state.State, auth common.Authorizer) (*AgentAPI, error) { |
1025 | + if !auth.IsLoggedIn() { |
1026 | + return nil, common.ErrNotLoggedIn |
1027 | + } |
1028 | + if !auth.AuthMachineAgent() { |
1029 | + return nil, common.ErrPerm |
1030 | + } |
1031 | + return &AgentAPI{ |
1032 | + st: st, |
1033 | + auth: auth, |
1034 | + }, nil |
1035 | +} |
1036 | + |
1037 | +func (api *AgentAPI) GetMachines(args params.Machines) (params.MachineAgentGetMachinesResults, error) { |
1038 | + results := params.MachineAgentGetMachinesResults{ |
1039 | + Machines: make([]params.MachineAgentGetMachinesResult, len(args.Ids)), |
1040 | + } |
1041 | + for i, id := range args.Ids { |
1042 | + result, err := api.getMachine(id) |
1043 | + result.Error = common.ServerError(err) |
1044 | + results.Machines[i] = result |
1045 | + } |
1046 | + return results, nil |
1047 | +} |
1048 | + |
1049 | +func (api *AgentAPI) getMachine(id string) (result params.MachineAgentGetMachinesResult, err error) { |
1050 | + // Allow only for the owner agent. |
1051 | + // Note: having a bulk API call for this is utter madness, given that |
1052 | + // this check means we can only ever return a single object. |
1053 | + if !api.auth.AuthOwner(state.MachineTag(id)) { |
1054 | + err = common.ErrPerm |
1055 | + return |
1056 | + } |
1057 | + machine, err := api.st.Machine(id) |
1058 | + if err != nil { |
1059 | + return |
1060 | + } |
1061 | + result.Life = params.Life(machine.Life().String()) |
1062 | + result.Jobs = stateJobsToAPIParamsJobs(machine.Jobs()) |
1063 | + return |
1064 | +} |
1065 | + |
1066 | +func stateJobsToAPIParamsJobs(jobs []state.MachineJob) []params.MachineJob { |
1067 | + pjobs := make([]params.MachineJob, len(jobs)) |
1068 | + for i, job := range jobs { |
1069 | + pjobs[i] = params.MachineJob(job.String()) |
1070 | + } |
1071 | + return pjobs |
1072 | +} |
1073 | |
1074 | === added file 'state/apiserver/machine/agent_test.go' |
1075 | --- state/apiserver/machine/agent_test.go 1970-01-01 00:00:00 +0000 |
1076 | +++ state/apiserver/machine/agent_test.go 2013-06-18 12:56:33 +0000 |
1077 | @@ -0,0 +1,91 @@ |
1078 | +package machine_test |
1079 | + |
1080 | +import ( |
1081 | + . "launchpad.net/gocheck" |
1082 | + "launchpad.net/juju-core/state/api" |
1083 | + "launchpad.net/juju-core/state/api/params" |
1084 | + "launchpad.net/juju-core/state/apiserver/machine" |
1085 | +) |
1086 | + |
1087 | +type agentSuite struct { |
1088 | + commonSuite |
1089 | + agent *machine.AgentAPI |
1090 | +} |
1091 | + |
1092 | +var _ = Suite(&agentSuite{}) |
1093 | + |
1094 | +func (s *agentSuite) SetUpTest(c *C) { |
1095 | + s.commonSuite.SetUpTest(c) |
1096 | + |
1097 | + // Create a machiner API for machine 1. |
1098 | + api, err := machine.NewAgentAPI( |
1099 | + s.State, |
1100 | + s.authorizer, |
1101 | + ) |
1102 | + c.Assert(err, IsNil) |
1103 | + s.agent = api |
1104 | +} |
1105 | + |
1106 | +func (s *agentSuite) TestAgentFailsWithNonMachineAgentUser(c *C) { |
1107 | + auth := s.authorizer |
1108 | + auth.machineAgent = false |
1109 | + api, err := machine.NewAgentAPI(s.State, auth) |
1110 | + c.Assert(err, NotNil) |
1111 | + c.Assert(api, IsNil) |
1112 | + c.Assert(err, ErrorMatches, "permission denied") |
1113 | +} |
1114 | + |
1115 | +func (s *agentSuite) TestAgentFailsWhenNotLoggedIn(c *C) { |
1116 | + auth := s.authorizer |
1117 | + auth.loggedIn = false |
1118 | + api, err := machine.NewAgentAPI(s.State, auth) |
1119 | + c.Assert(err, NotNil) |
1120 | + c.Assert(api, IsNil) |
1121 | + c.Assert(err, ErrorMatches, "not logged in") |
1122 | +} |
1123 | + |
1124 | +func (s *agentSuite) TestGetMachines(c *C) { |
1125 | + err := s.machine1.Destroy() |
1126 | + c.Assert(err, IsNil) |
1127 | + results, err := s.agent.GetMachines(params.Machines{ |
1128 | + Ids: []string{"1", "0", "42"}, |
1129 | + }) |
1130 | + c.Assert(err, IsNil) |
1131 | + c.Assert(results, DeepEquals, params.MachineAgentGetMachinesResults{ |
1132 | + Machines: []params.MachineAgentGetMachinesResult{{ |
1133 | + Life: "dying", |
1134 | + Jobs: []params.MachineJob{params.JobHostUnits}, |
1135 | + }, { |
1136 | + Error: ¶ms.Error{ |
1137 | + Code: api.CodeUnauthorized, |
1138 | + Message: "permission denied", |
1139 | + }, |
1140 | + }, { |
1141 | + Error: ¶ms.Error{ |
1142 | + Code: api.CodeUnauthorized, |
1143 | + Message: "permission denied", |
1144 | + }, |
1145 | + }}, |
1146 | + }) |
1147 | +} |
1148 | + |
1149 | +func (s *agentSuite) TestGetNotFoundMachine(c *C) { |
1150 | + err := s.machine1.Destroy() |
1151 | + c.Assert(err, IsNil) |
1152 | + err = s.machine1.EnsureDead() |
1153 | + c.Assert(err, IsNil) |
1154 | + err = s.machine1.Remove() |
1155 | + c.Assert(err, IsNil) |
1156 | + results, err := s.agent.GetMachines(params.Machines{ |
1157 | + Ids: []string{"1"}, |
1158 | + }) |
1159 | + c.Assert(err, IsNil) |
1160 | + c.Assert(results, DeepEquals, params.MachineAgentGetMachinesResults{ |
1161 | + Machines: []params.MachineAgentGetMachinesResult{{ |
1162 | + Error: ¶ms.Error{ |
1163 | + Code: api.CodeNotFound, |
1164 | + Message: "machine 1 not found", |
1165 | + }, |
1166 | + }}, |
1167 | + }) |
1168 | +} |
1169 | |
1170 | === added file 'state/apiserver/machine/common_test.go' |
1171 | --- state/apiserver/machine/common_test.go 1970-01-01 00:00:00 +0000 |
1172 | +++ state/apiserver/machine/common_test.go 2013-06-18 12:56:33 +0000 |
1173 | @@ -0,0 +1,66 @@ |
1174 | +package machine_test |
1175 | + |
1176 | +import ( |
1177 | + . "launchpad.net/gocheck" |
1178 | + "launchpad.net/juju-core/juju/testing" |
1179 | + "launchpad.net/juju-core/state" |
1180 | + coretesting "launchpad.net/juju-core/testing" |
1181 | + stdtesting "testing" |
1182 | +) |
1183 | + |
1184 | +func Test(t *stdtesting.T) { |
1185 | + coretesting.MgoTestPackage(t) |
1186 | +} |
1187 | + |
1188 | +type commonSuite struct { |
1189 | + testing.JujuConnSuite |
1190 | + |
1191 | + authorizer fakeAuthorizer |
1192 | + |
1193 | + machine0 *state.Machine |
1194 | + machine1 *state.Machine |
1195 | +} |
1196 | + |
1197 | +func (s *commonSuite) SetUpTest(c *C) { |
1198 | + s.JujuConnSuite.SetUpTest(c) |
1199 | + |
1200 | + var err error |
1201 | + s.machine0, err = s.State.AddMachine("series", state.JobManageEnviron, state.JobManageState) |
1202 | + c.Assert(err, IsNil) |
1203 | + |
1204 | + s.machine1, err = s.State.AddMachine("series", state.JobHostUnits) |
1205 | + c.Assert(err, IsNil) |
1206 | + |
1207 | + // Create a fakeAuthorizer so we can check permissions, |
1208 | + // set up assuming machine 1 has logged in. |
1209 | + s.authorizer = fakeAuthorizer{ |
1210 | + tag: state.MachineTag(s.machine1.Id()), |
1211 | + loggedIn: true, |
1212 | + manager: false, |
1213 | + machineAgent: true, |
1214 | + } |
1215 | +} |
1216 | + |
1217 | +// fakeAuthorizer implements the common.Authorizer interface. |
1218 | +type fakeAuthorizer struct { |
1219 | + tag string |
1220 | + loggedIn bool |
1221 | + manager bool |
1222 | + machineAgent bool |
1223 | +} |
1224 | + |
1225 | +func (fa fakeAuthorizer) IsLoggedIn() bool { |
1226 | + return fa.loggedIn |
1227 | +} |
1228 | + |
1229 | +func (fa fakeAuthorizer) AuthOwner(tag string) bool { |
1230 | + return fa.tag == tag |
1231 | +} |
1232 | + |
1233 | +func (fa fakeAuthorizer) AuthEnvironManager() bool { |
1234 | + return fa.manager |
1235 | +} |
1236 | + |
1237 | +func (fa fakeAuthorizer) AuthMachineAgent() bool { |
1238 | + return fa.machineAgent |
1239 | +} |
1240 | |
1241 | === added file 'state/apiserver/machine/machine.test' |
1242 | Binary files state/apiserver/machine/machine.test 1970-01-01 00:00:00 +0000 and state/apiserver/machine/machine.test 2013-06-18 12:56:33 +0000 differ |
1243 | === modified file 'state/apiserver/machine/machiner.go' |
1244 | --- state/apiserver/machiner/machiner.go 2013-06-06 17:09:49 +0000 |
1245 | +++ state/apiserver/machine/machiner.go 2013-06-18 12:56:33 +0000 |
1246 | @@ -1,7 +1,7 @@ |
1247 | // Copyright 2013 Canonical Ltd. |
1248 | // Licensed under the AGPLv3, see LICENCE file for details. |
1249 | |
1250 | -package machiner |
1251 | +package machine |
1252 | |
1253 | import ( |
1254 | "launchpad.net/juju-core/state" |
1255 | @@ -10,24 +10,24 @@ |
1256 | ) |
1257 | |
1258 | // Machiner implements the API used by the machiner worker. |
1259 | -type Machiner struct { |
1260 | +type MachinerAPI struct { |
1261 | st *state.State |
1262 | auth common.Authorizer |
1263 | } |
1264 | |
1265 | -// New creates a new instance of the Machiner facade. |
1266 | -func New(st *state.State, authorizer common.Authorizer) (*Machiner, error) { |
1267 | +// NewMachinerAPI creates a new instance of the Machiner API. |
1268 | +func NewMachinerAPI(st *state.State, authorizer common.Authorizer) (*MachinerAPI, error) { |
1269 | if !authorizer.IsLoggedIn() { |
1270 | return nil, common.ErrNotLoggedIn |
1271 | } |
1272 | if !authorizer.AuthMachineAgent() { |
1273 | return nil, common.ErrPerm |
1274 | } |
1275 | - return &Machiner{st, authorizer}, nil |
1276 | + return &MachinerAPI{st, authorizer}, nil |
1277 | } |
1278 | |
1279 | // SetStatus sets the status of each given machine. |
1280 | -func (m *Machiner) SetStatus(args params.MachinesSetStatus) (params.ErrorResults, error) { |
1281 | +func (m *MachinerAPI) SetStatus(args params.MachinesSetStatus) (params.ErrorResults, error) { |
1282 | result := params.ErrorResults{ |
1283 | Errors: make([]*params.Error, len(args.Machines)), |
1284 | } |
1285 | @@ -38,7 +38,7 @@ |
1286 | machine, err := m.st.Machine(arg.Id) |
1287 | if err == nil { |
1288 | // Allow only for the owner agent. |
1289 | - if !m.auth.AuthOwner(machine) { |
1290 | + if !m.auth.AuthOwner(machine.Tag()) { |
1291 | err = common.ErrPerm |
1292 | } else { |
1293 | err = machine.SetStatus(arg.Status, arg.Info) |
1294 | @@ -50,12 +50,12 @@ |
1295 | } |
1296 | |
1297 | // Watch starts an EntityWatcher for each given machine. |
1298 | -//func (m *Machiner) Watch(args params.Machines) (params.MachinerWatchResults, error) { |
1299 | +//func (m *MachinerAPI) Watch(args params.Machines) (params.MachinerWatchResults, error) { |
1300 | // TODO (dimitern) implement this once the watchers can handle bulk ops |
1301 | //} |
1302 | |
1303 | // Life returns the lifecycle state of each given machine. |
1304 | -func (m *Machiner) Life(args params.Machines) (params.MachinesLifeResults, error) { |
1305 | +func (m *MachinerAPI) Life(args params.Machines) (params.MachinesLifeResults, error) { |
1306 | result := params.MachinesLifeResults{ |
1307 | Machines: make([]params.MachineLifeResult, len(args.Ids)), |
1308 | } |
1309 | @@ -66,7 +66,7 @@ |
1310 | machine, err := m.st.Machine(id) |
1311 | if err == nil { |
1312 | // Allow only for the owner agent. |
1313 | - if !m.auth.AuthOwner(machine) { |
1314 | + if !m.auth.AuthOwner(machine.Tag()) { |
1315 | err = common.ErrPerm |
1316 | } else { |
1317 | result.Machines[i].Life = params.Life(machine.Life().String()) |
1318 | @@ -79,7 +79,7 @@ |
1319 | |
1320 | // EnsureDead changes the lifecycle of each given machine to Dead if |
1321 | // it's Alive or Dying. It does nothing otherwise. |
1322 | -func (m *Machiner) EnsureDead(args params.Machines) (params.ErrorResults, error) { |
1323 | +func (m *MachinerAPI) EnsureDead(args params.Machines) (params.ErrorResults, error) { |
1324 | result := params.ErrorResults{ |
1325 | Errors: make([]*params.Error, len(args.Ids)), |
1326 | } |
1327 | @@ -90,7 +90,7 @@ |
1328 | machine, err := m.st.Machine(id) |
1329 | if err == nil { |
1330 | // Allow only for the owner agent. |
1331 | - if !m.auth.AuthOwner(machine) { |
1332 | + if !m.auth.AuthOwner(machine.Tag()) { |
1333 | err = common.ErrPerm |
1334 | } else { |
1335 | err = machine.EnsureDead() |
1336 | |
1337 | === modified file 'state/apiserver/machine/machiner_test.go' |
1338 | --- state/apiserver/machiner/machiner_test.go 2013-06-06 17:09:49 +0000 |
1339 | +++ state/apiserver/machine/machiner_test.go 2013-06-18 12:56:33 +0000 |
1340 | @@ -1,85 +1,33 @@ |
1341 | // Copyright 2013 Canonical Ltd. |
1342 | // Licensed under the AGPLv3, see LICENCE file for details. |
1343 | |
1344 | -package machiner_test |
1345 | +package machine_test |
1346 | |
1347 | import ( |
1348 | . "launchpad.net/gocheck" |
1349 | - "launchpad.net/juju-core/juju/testing" |
1350 | "launchpad.net/juju-core/state" |
1351 | "launchpad.net/juju-core/state/api" |
1352 | "launchpad.net/juju-core/state/api/params" |
1353 | - "launchpad.net/juju-core/state/apiserver/common" |
1354 | - "launchpad.net/juju-core/state/apiserver/machiner" |
1355 | - coretesting "launchpad.net/juju-core/testing" |
1356 | - stdtesting "testing" |
1357 | + "launchpad.net/juju-core/state/apiserver/machine" |
1358 | ) |
1359 | |
1360 | -func Test(t *stdtesting.T) { |
1361 | - coretesting.MgoTestPackage(t) |
1362 | -} |
1363 | - |
1364 | type machinerSuite struct { |
1365 | - testing.JujuConnSuite |
1366 | - |
1367 | - authorizer *fakeAuthorizer |
1368 | - machiner *machiner.Machiner |
1369 | - |
1370 | - machine0 *state.Machine |
1371 | - machine1 *state.Machine |
1372 | + commonSuite |
1373 | + machiner *machine.MachinerAPI |
1374 | } |
1375 | |
1376 | var _ = Suite(&machinerSuite{}) |
1377 | |
1378 | -// fakeAuthorizer implements the common.Authorizer interface. |
1379 | -type fakeAuthorizer struct { |
1380 | - tag string |
1381 | - loggedIn bool |
1382 | - manager bool |
1383 | - machineAgent bool |
1384 | -} |
1385 | - |
1386 | -func (fa *fakeAuthorizer) IsLoggedIn() bool { |
1387 | - return fa.loggedIn |
1388 | -} |
1389 | - |
1390 | -func (fa *fakeAuthorizer) AuthOwner(entity common.Tagger) bool { |
1391 | - return entity.Tag() == fa.tag |
1392 | -} |
1393 | - |
1394 | -func (fa *fakeAuthorizer) AuthEnvironManager() bool { |
1395 | - return fa.manager |
1396 | -} |
1397 | - |
1398 | -func (fa *fakeAuthorizer) AuthMachineAgent() bool { |
1399 | - return fa.machineAgent |
1400 | -} |
1401 | - |
1402 | func (s *machinerSuite) SetUpTest(c *C) { |
1403 | - s.JujuConnSuite.SetUpTest(c) |
1404 | - |
1405 | - // Create a machine so that we can login as its agent |
1406 | - var err error |
1407 | - s.machine0, err = s.State.AddMachine("series", state.JobManageEnviron) |
1408 | - c.Assert(err, IsNil) |
1409 | - // Add another normal machine |
1410 | - s.machine1, err = s.State.AddMachine("series", state.JobHostUnits) |
1411 | - c.Assert(err, IsNil) |
1412 | - |
1413 | - // Create a fakeAuthorizer so we can check permissions. |
1414 | - s.authorizer = &fakeAuthorizer{ |
1415 | - tag: state.MachineTag(s.machine1.Id()), |
1416 | - loggedIn: true, |
1417 | - manager: false, |
1418 | - machineAgent: true, |
1419 | - } |
1420 | + s.commonSuite.SetUpTest(c) |
1421 | |
1422 | // Create a machiner API for machine 1. |
1423 | - s.machiner, err = machiner.New( |
1424 | + machiner, err := machine.NewMachinerAPI( |
1425 | s.State, |
1426 | s.authorizer, |
1427 | ) |
1428 | c.Assert(err, IsNil) |
1429 | + s.machiner = machiner |
1430 | } |
1431 | |
1432 | func (s *machinerSuite) assertError(c *C, err *params.Error, code, messageRegexp string) { |
1433 | @@ -91,7 +39,7 @@ |
1434 | func (s *machinerSuite) TestMachinerFailsWithNonMachineAgentUser(c *C) { |
1435 | anAuthorizer := s.authorizer |
1436 | anAuthorizer.machineAgent = false |
1437 | - aMachiner, err := machiner.New(s.State, anAuthorizer) |
1438 | + aMachiner, err := machine.NewMachinerAPI(s.State, anAuthorizer) |
1439 | c.Assert(err, NotNil) |
1440 | c.Assert(aMachiner, IsNil) |
1441 | c.Assert(err, ErrorMatches, "permission denied") |
1442 | @@ -100,7 +48,7 @@ |
1443 | func (s *machinerSuite) TestMachinerFailsWhenNotLoggedIn(c *C) { |
1444 | anAuthorizer := s.authorizer |
1445 | anAuthorizer.loggedIn = false |
1446 | - aMachiner, err := machiner.New(s.State, anAuthorizer) |
1447 | + aMachiner, err := machine.NewMachinerAPI(s.State, anAuthorizer) |
1448 | c.Assert(err, NotNil) |
1449 | c.Assert(aMachiner, IsNil) |
1450 | c.Assert(err, ErrorMatches, "not logged in") |
1451 | |
1452 | === removed file 'state/apiserver/resource.go' |
1453 | --- state/apiserver/resource.go 2013-05-24 19:03:39 +0000 |
1454 | +++ state/apiserver/resource.go 1970-01-01 00:00:00 +0000 |
1455 | @@ -1,82 +0,0 @@ |
1456 | -// Copyright 2013 Canonical Ltd. |
1457 | -// Licensed under the AGPLv3, see LICENCE file for details. |
1458 | - |
1459 | -package apiserver |
1460 | - |
1461 | -import ( |
1462 | - "launchpad.net/juju-core/log" |
1463 | - "strconv" |
1464 | - "sync" |
1465 | -) |
1466 | - |
1467 | -// resource represents the interface provided by state watchers and pingers. |
1468 | -type resource interface { |
1469 | - Stop() error |
1470 | -} |
1471 | - |
1472 | -// resources holds all the resources for a connection. |
1473 | -type resources struct { |
1474 | - mu sync.Mutex |
1475 | - maxId uint64 |
1476 | - rs map[string]*srvResource |
1477 | -} |
1478 | - |
1479 | -// srvResource holds the details of a resource. It also implements the |
1480 | -// Stop RPC method for all resources. |
1481 | -type srvResource struct { |
1482 | - rs *resources |
1483 | - resource resource |
1484 | - id string |
1485 | -} |
1486 | - |
1487 | -// Stop stops the given resource. It causes any outstanding |
1488 | -// Next calls to return a CodeStopped error. |
1489 | -// Any subsequent Next calls will return a CodeNotFound |
1490 | -// error because the resource will no longer exist. |
1491 | -func (r *srvResource) Stop() error { |
1492 | - err := r.resource.Stop() |
1493 | - r.rs.mu.Lock() |
1494 | - defer r.rs.mu.Unlock() |
1495 | - delete(r.rs.rs, r.id) |
1496 | - return err |
1497 | -} |
1498 | - |
1499 | -func newResources() *resources { |
1500 | - return &resources{ |
1501 | - rs: make(map[string]*srvResource), |
1502 | - } |
1503 | -} |
1504 | - |
1505 | -// get returns the srvResource registered with the given |
1506 | -// id, or nil if there is no such resource. |
1507 | -func (rs *resources) get(id string) *srvResource { |
1508 | - rs.mu.Lock() |
1509 | - defer rs.mu.Unlock() |
1510 | - return rs.rs[id] |
1511 | -} |
1512 | - |
1513 | -// register records the given watcher and returns |
1514 | -// a srvResource instance for it. |
1515 | -func (rs *resources) register(r resource) *srvResource { |
1516 | - rs.mu.Lock() |
1517 | - defer rs.mu.Unlock() |
1518 | - rs.maxId++ |
1519 | - sr := &srvResource{ |
1520 | - rs: rs, |
1521 | - id: strconv.FormatUint(rs.maxId, 10), |
1522 | - resource: r, |
1523 | - } |
1524 | - rs.rs[sr.id] = sr |
1525 | - return sr |
1526 | -} |
1527 | - |
1528 | -func (rs *resources) stopAll() { |
1529 | - rs.mu.Lock() |
1530 | - defer rs.mu.Unlock() |
1531 | - for _, r := range rs.rs { |
1532 | - if err := r.resource.Stop(); err != nil { |
1533 | - log.Errorf("state/api: error stopping %T resource: %v", r, err) |
1534 | - } |
1535 | - } |
1536 | - rs.rs = make(map[string]*srvResource) |
1537 | -} |
1538 | |
1539 | === modified file 'state/apiserver/root.go' |
1540 | --- state/apiserver/root.go 2013-06-06 17:09:49 +0000 |
1541 | +++ state/apiserver/root.go 2013-06-18 12:56:33 +0000 |
1542 | @@ -6,7 +6,7 @@ |
1543 | import ( |
1544 | "launchpad.net/juju-core/state" |
1545 | "launchpad.net/juju-core/state/apiserver/common" |
1546 | - "launchpad.net/juju-core/state/apiserver/machiner" |
1547 | + "launchpad.net/juju-core/state/apiserver/machine" |
1548 | "launchpad.net/juju-core/state/multiwatcher" |
1549 | ) |
1550 | |
1551 | @@ -83,15 +83,24 @@ |
1552 | return nil |
1553 | } |
1554 | |
1555 | -// Machiner returns an object that provides access to the Machiner API |
1556 | -// facade. The id argument is reserved for future use and currently |
1557 | -// needs to be empty. |
1558 | -func (r *srvRoot) Machiner(id string) (*machiner.Machiner, error) { |
1559 | - if id != "" { |
1560 | - // Safeguard id for possible future use. |
1561 | - return nil, common.ErrBadId |
1562 | - } |
1563 | - return machiner.New(r.srv.state, r) |
1564 | +// Machiner returns an object that provides access to the Machiner API. |
1565 | +// The id argument is reserved for future use and must currently |
1566 | +// be empty. |
1567 | +func (r *srvRoot) Machiner(id string) (*machine.MachinerAPI, error) { |
1568 | + if id != "" { |
1569 | + return nil, common.ErrBadId |
1570 | + } |
1571 | + return machine.NewMachinerAPI(r.srv.state, r) |
1572 | +} |
1573 | + |
1574 | +// MachineAgent returns an object that provides access to the machine |
1575 | +// agent API. The id argument is reserved for future use and must currently |
1576 | +// be empty. |
1577 | +func (r *srvRoot) MachineAgent(id string) (*machine.AgentAPI, error) { |
1578 | + if id != "" { |
1579 | + return nil, common.ErrBadId |
1580 | + } |
1581 | + return machine.NewAgentAPI(r.srv.state, r) |
1582 | } |
1583 | |
1584 | // User returns an object that provides |
1585 | @@ -237,10 +246,10 @@ |
1586 | } |
1587 | |
1588 | // AuthOwner returns whether the authenticated user's tag matches the |
1589 | -// given entity's tag. |
1590 | -func (r *srvRoot) AuthOwner(entity common.Tagger) bool { |
1591 | +// given entity tag. |
1592 | +func (r *srvRoot) AuthOwner(tag string) bool { |
1593 | authUser := r.user.authenticator() |
1594 | - return authUser.Tag() == entity.Tag() |
1595 | + return authUser.Tag() == tag |
1596 | } |
1597 | |
1598 | // AuthEnvironManager returns whether the authenticated user is a |
1599 | |
1600 | === renamed file 'state/apiserver/apiserver.go' => 'state/apiserver/server.go' |
1601 | === added directory 'state/apiserver/testing' |
1602 | === modified file 'state/machine.go' |
1603 | --- state/machine.go 2013-06-18 12:56:33 +0000 |
1604 | +++ state/machine.go 2013-06-18 12:56:33 +0000 |
1605 | @@ -32,13 +32,13 @@ |
1606 | _ MachineJob = iota |
1607 | JobHostUnits |
1608 | JobManageEnviron |
1609 | - JobServeAPI |
1610 | + JobManageState |
1611 | ) |
1612 | |
1613 | -var jobNames = []string{ |
1614 | - JobHostUnits: "JobHostUnits", |
1615 | - JobManageEnviron: "JobManageEnviron", |
1616 | - JobServeAPI: "JobServeAPI", |
1617 | +var jobNames = []params.MachineJob{ |
1618 | + JobHostUnits: params.JobHostUnits, |
1619 | + JobManageEnviron: params.JobManageEnviron, |
1620 | + JobManageState: params.JobManageState, |
1621 | } |
1622 | |
1623 | func (job MachineJob) String() string { |
1624 | @@ -46,7 +46,7 @@ |
1625 | if j <= 0 || j >= len(jobNames) { |
1626 | return fmt.Sprintf("<unknown job %d>", j) |
1627 | } |
1628 | - return jobNames[j] |
1629 | + return string(jobNames[j]) |
1630 | } |
1631 | |
1632 | // machineDoc represents the internal state of a machine in MongoDB. |
1633 | |
1634 | === modified file 'state/state_test.go' |
1635 | --- state/state_test.go 2013-06-18 12:56:33 +0000 |
1636 | +++ state/state_test.go 2013-06-18 12:56:33 +0000 |
1637 | @@ -117,7 +117,7 @@ |
1638 | }{ |
1639 | {state.JobHostUnits, "JobHostUnits"}, |
1640 | {state.JobManageEnviron, "JobManageEnviron"}, |
1641 | - {state.JobServeAPI, "JobServeAPI"}, |
1642 | + {state.JobManageState, "JobManageState"}, |
1643 | {0, "<unknown job 0>"}, |
1644 | {5, "<unknown job 5>"}, |
1645 | } |
1646 | @@ -155,7 +155,7 @@ |
1647 | allJobs := []state.MachineJob{ |
1648 | state.JobHostUnits, |
1649 | state.JobManageEnviron, |
1650 | - state.JobServeAPI, |
1651 | + state.JobManageState, |
1652 | } |
1653 | m1, err := s.State.AddMachine("blahblah", allJobs...) |
1654 | c.Assert(err, IsNil) |
Reviewers: mp+165705_ code.launchpad. net,
Message:
Please take a look.
Description:
api: implement Machine.Jobs
https:/ /code.launchpad .net/~rogpeppe/ juju-core/ 312-api- jobs/+merge/ 165705
Requires: /code.launchpad .net/~rogpeppe/ juju-core/ 311-juju- bootstrap- state-change- password- 1.5/+merge/ 165675
https:/
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/9754043/
Affected files: machine. go params/ params. go /machine_ test.go /utils. go
A [revision details]
M state/api/
M state/api/
M state/apiserver
M state/apiserver
M state/machine.go
Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: <email address hidden>
+New revision: <email address hidden>
Index: state/machine.go
=== modified file 'state/machine.go'
--- state/machine.go 2013-05-21 16:40:29 +0000
+++ state/machine.go 2013-05-24 21:38:13 +0000
@@ -36,7 +36,7 @@
JobServeAPI
)
-var jobNames = []string{ MachineJob{ iron: "JobManageEnviron", "<unknown job %d>", j)
+var jobNames = []params.
JobHostUnits: "JobHostUnits",
JobManageEnv
JobServeAPI: "JobServeAPI",
@@ -47,7 +47,7 @@
if j <= 0 || j >= len(jobNames) {
return fmt.Sprintf(
}
- return jobNames[j]
+ return string(jobNames[j])
}
// machineDoc represents the internal state of a machine in MongoDB.
Index: state/api/ machine. go api/machine. go' machine. go 2013-05-24 19:03:39 +0000 machine. go 2013-05-24 21:38:13 +0000
=== modified file 'state/
--- state/api/
+++ state/api/
@@ -139,6 +139,11 @@
return m.doc.Life
}
+// Jobs returns the responsibilities that must be fulfilled by m's agent.
+func (m *Machine) Jobs() []params.MachineJob {
+ return m.doc.Jobs
+}
+
// Series returns the operating system series running on the machine.
func (m *Machine) Series() string {
return m.doc.Series
Index: state/apiserver /machine_ test.go apiserver/ machine_ test.go' /machine_ test.go 2013-05-24 19:03:39 +0000 /machine_ test.go 2013-05-24 21:38:13 +0000 string( life), Equals, "dead")
=== modified file 'state/
--- state/apiserver
+++ state/apiserver
@@ -212,6 +212,29 @@
c.Assert(
}
+func (s *suite) TestMachineJobs(c *C) { Environ, ord(c, stm) stm.Id( )) MachineJob{ JobHostUnits, JobManageEnviro n, reDead( c *C) { AddMachine( "series" , state.JobHostUnits)
+ stm, err := s.State.AddMachine(
+ "series",
+ state.JobHostUnits,
+ state.JobManage
+ state.JobServeAPI,
+ )
+ c.Assert(err, IsNil)
+ setDefaultPassw
+
+ st := s.openAs(c, stm.Tag())
+ defer st.Close()
+
+ m, err := st.Machine(
+ c.Assert(err, IsNil)
+
+ c.Assert(m.Jobs(), DeepEquals, []params.
+ params.
+ params.
+ params.JobServeAPI,
+ })
+}
+
func (s *suite) TestMachineEnsu
stm, err := s.State.
c.Assert(err, IsNil)
Index: state/apiserver /utils. go apiserver/ utils.go' /utils. go 2013-05-24 19:11:19 +0000 /utils. go 2013-05-24 21:38:13 +0000
=== modified file 'state/
--- state/apiserver
+++ state/apiserver
@@ -44,10 +44,16 @@
return nil
}
instId, _ := stm.InstanceId()
+ jobs := stm....