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