Merge lp:~wallyworld/juju-core/machiner-access-for-uniter into lp:~go-bot/juju-core/trunk

Proposed by Ian Booth
Status: Rejected
Rejected by: William Reade
Proposed branch: lp:~wallyworld/juju-core/machiner-access-for-uniter
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 345 lines (+245/-21)
5 files modified
state/apiserver/common/auth.go (+38/-0)
state/apiserver/machine/common_test.go (+7/-7)
state/apiserver/machine/machiner.go (+2/-4)
state/apiserver/machine/machiner_test.go (+10/-10)
state/apiserver/machine/uniter_test.go (+188/-0)
To merge this branch: bzr merge lp:~wallyworld/juju-core/machiner-access-for-uniter
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+205290@code.launchpad.net

Description of the change

Allow assigned units read access to machines

This branch allows unit agents to use the machiner
API to get a machine object for their assigned
machine. The server side api looks up the machine
life and the necessary auth function is modified
to allow assigned units as well as the machine
owner.

The implementation is done as a helper function
because a downstream branch also uses it. This
work will be used by the unit agent upgrade worker.

https://codereview.appspot.com/60920043/

To post a comment you must log in.
Revision history for this message
Ian Booth (wallyworld) wrote :

Reviewers: mp+205290_code.launchpad.net,

Message:
Please take a look.

Description:
Allow assigned units read access to machines

This branch allows unit agents to use the machiner
API to get a machine object for their assigned
machine. The server side api looks up the machine
life and the necessary auth function is modified
to allow assigned units as well as the machine
owner.

The implementation is done as a helper function
because a downstream branch also uses it. This
work will be used by the unit agent upgrade worker.

https://code.launchpad.net/~wallyworld/juju-core/machiner-access-for-uniter/+merge/205290

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/60920043/

Affected files (+224, -21 lines):
   A [revision details]
   A state/apiserver/common/auth.go
   M state/apiserver/machine/common_test.go
   M state/apiserver/machine/machiner.go
   M state/apiserver/machine/machiner_test.go

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Looks great so far, I have just a few suggestions.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/common/auth.go
File state/apiserver/common/auth.go (right):

https://codereview.appspot.com/60920043/diff/1/state/apiserver/common/auth.go#newcode13
state/apiserver/common/auth.go:13: // OwnerOrDeployedUnitAuthFunc
provides an AuthFunc which returns true if the caller
I still think this ought to be in the Authorizer interfaces, and should
be called AuthOwnerOrDeployedUnit. Then, you can implement it in
apiserver/root.go, as the other auth funcs, and it could be simplified
as well (no need to call GetAuthTag and GetAuthEntity, because you have
direct access to both).

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go
File state/apiserver/machine/machiner_test.go (right):

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode215
state/apiserver/machine/machiner_test.go:215: // Test that a unit agent
has read access to the assigned machine for it's unit.
Please put this suite in a separate file, like uniter_test.go.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode309
state/apiserver/machine/machiner_test.go:309: {Tag: "machine-1"},
I'd add "machine-0" and "machine-42" to the list of entities, just to be
extra careful and check for ErrPerm.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode334
state/apiserver/machine/machiner_test.go:334: {Tag: "machine-1",
Addresses: addresses},
ditto

https://codereview.appspot.com/60920043/

2305. By Ian Booth

Merge trunk

2306. By Ian Booth

Refactor some tests

Revision history for this message
Ian Booth (wallyworld) wrote :

Please take a look.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/common/auth.go
File state/apiserver/common/auth.go (right):

https://codereview.appspot.com/60920043/diff/1/state/apiserver/common/auth.go#newcode13
state/apiserver/common/auth.go:13: // OwnerOrDeployedUnitAuthFunc
provides an AuthFunc which returns true if the caller
On 2014/02/10 08:41:29, dimitern wrote:
> I still think this ought to be in the Authorizer interfaces, and
should be
> called AuthOwnerOrDeployedUnit. Then, you can implement it in
apiserver/root.go,
> as the other auth funcs, and it could be simplified as well (no need
to call
> GetAuthTag and GetAuthEntity, because you have direct access to both).

That doesn't work so well. The OwnerOrDeployedUnitAuthFunc func is a
little involved, and also needs to be implemented properly on the
FakeAuthorizer. It's better to have a common helper function and pass in
the authorizer to use, allowing the OwnerOrDeployedUnitAuthFunc to use
the primitives offered by the respective authorisers. As an aside, there
a similarly complex bit of auth logic in deployer which could also be
pulled out as a separate function.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go
File state/apiserver/machine/machiner_test.go (right):

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode215
state/apiserver/machine/machiner_test.go:215: // Test that a unit agent
has read access to the assigned machine for it's unit.
On 2014/02/10 08:41:29, dimitern wrote:
> Please put this suite in a separate file, like uniter_test.go.

Done.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode309
state/apiserver/machine/machiner_test.go:309: {Tag: "machine-1"},
On 2014/02/10 08:41:29, dimitern wrote:
> I'd add "machine-0" and "machine-42" to the list of entities, just to
be extra
> careful and check for ErrPerm.

Done.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode309
state/apiserver/machine/machiner_test.go:309: {Tag: "machine-1"},
On 2014/02/10 08:41:29, dimitern wrote:
> I'd add "machine-0" and "machine-42" to the list of entities, just to
be extra
> careful and check for ErrPerm.

Done.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode334
state/apiserver/machine/machiner_test.go:334: {Tag: "machine-1",
Addresses: addresses},
On 2014/02/10 08:41:29, dimitern wrote:
> ditto

Done.

https://codereview.appspot.com/60920043/diff/1/state/apiserver/machine/machiner_test.go#newcode334
state/apiserver/machine/machiner_test.go:334: {Tag: "machine-1",
Addresses: addresses},
On 2014/02/10 08:41:29, dimitern wrote:
> ditto

Done.

https://codereview.appspot.com/60920043/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

The diff is screwed, but otherwise LGTM assuming you made what you said.

https://codereview.appspot.com/60920043/

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

On 2014/02/11 09:53:47, dimitern wrote:
> The diff is screwed, but otherwise LGTM assuming you made what you
said.

as discussed live, the upgrader doesn't need changes so not lgtm

https://codereview.appspot.com/60920043/

Unmerged revisions

2306. By Ian Booth

Refactor some tests

2305. By Ian Booth

Merge trunk

2304. By Ian Booth

Unit agents have read access to machines on which their units are deployed

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'state/apiserver/common/auth.go'
2--- state/apiserver/common/auth.go 1970-01-01 00:00:00 +0000
3+++ state/apiserver/common/auth.go 2014-02-11 01:14:25 +0000
4@@ -0,0 +1,38 @@
5+// Copyright 2014 Canonical Ltd.
6+// Licensed under the AGPLv3, see LICENCE file for details.
7+
8+package common
9+
10+import (
11+ "fmt"
12+
13+ "launchpad.net/juju-core/names"
14+ "launchpad.net/juju-core/state"
15+)
16+
17+// OwnerOrDeployedUnitAuthFunc provides an AuthFunc which returns true if the caller
18+// is the machine owner or a unit agent for a unit which is deployed on the machine.
19+func OwnerOrDeployedUnitAuthFunc(authorizer Authorizer) func() (AuthFunc, error) {
20+ return func() (AuthFunc, error) {
21+ tag := authorizer.GetAuthTag()
22+ kind, _, err := names.ParseTag(tag, "")
23+ if err != nil {
24+ return nil, err
25+ }
26+ if kind == names.MachineTagKind {
27+ return authorizer.AuthOwner, nil
28+ }
29+ unit, ok := authorizer.GetAuthEntity().(*state.Unit)
30+ if !ok {
31+ return nil, fmt.Errorf("%v is not a unit", authorizer.GetAuthTag())
32+ }
33+ id, err := unit.AssignedMachineId()
34+ if err != nil {
35+ return nil, err
36+ }
37+ return func(tag string) bool {
38+ assignedMachineTag := names.MachineTag(id)
39+ return tag == assignedMachineTag
40+ }, nil
41+ }
42+}
43
44=== modified file 'state/apiserver/machine/common_test.go'
45--- state/apiserver/machine/common_test.go 2014-01-22 19:28:08 +0000
46+++ state/apiserver/machine/common_test.go 2014-02-11 01:14:25 +0000
47@@ -7,6 +7,8 @@
48
49 "launchpad.net/juju-core/juju/testing"
50 "launchpad.net/juju-core/state"
51+ "launchpad.net/juju-core/state/apiserver/common"
52+ "launchpad.net/juju-core/state/apiserver/machine"
53 apiservertesting "launchpad.net/juju-core/state/apiserver/testing"
54 coretesting "launchpad.net/juju-core/testing"
55 )
56@@ -19,6 +21,8 @@
57 testing.JujuConnSuite
58
59 authorizer apiservertesting.FakeAuthorizer
60+ resources *common.Resources
61+ machiner *machine.MachinerAPI
62
63 machine0 *state.Machine
64 machine1 *state.Machine
65@@ -34,11 +38,7 @@
66 s.machine1, err = s.State.AddMachine("quantal", state.JobHostUnits)
67 c.Assert(err, gc.IsNil)
68
69- // Create a FakeAuthorizer so we can check permissions,
70- // set up assuming machine 1 has logged in.
71- s.authorizer = apiservertesting.FakeAuthorizer{
72- Tag: s.machine1.Tag(),
73- LoggedIn: true,
74- MachineAgent: true,
75- }
76+ // Create the resource registry separately to track invocations to
77+ // Register.
78+ s.resources = common.NewResources()
79 }
80
81=== modified file 'state/apiserver/machine/machiner.go'
82--- state/apiserver/machine/machiner.go 2014-01-20 21:29:08 +0000
83+++ state/apiserver/machine/machiner.go 2014-02-11 01:14:25 +0000
84@@ -27,15 +27,13 @@
85
86 // NewMachinerAPI creates a new instance of the Machiner API.
87 func NewMachinerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*MachinerAPI, error) {
88- if !authorizer.AuthMachineAgent() {
89+ if !authorizer.AuthMachineAgent() && !authorizer.AuthUnitAgent() {
90 return nil, common.ErrPerm
91 }
92 getCanModify := func() (common.AuthFunc, error) {
93 return authorizer.AuthOwner, nil
94 }
95- getCanRead := func() (common.AuthFunc, error) {
96- return authorizer.AuthOwner, nil
97- }
98+ getCanRead := common.OwnerOrDeployedUnitAuthFunc(authorizer)
99 return &MachinerAPI{
100 LifeGetter: common.NewLifeGetter(st, getCanRead),
101 StatusSetter: common.NewStatusSetter(st, getCanModify),
102
103=== modified file 'state/apiserver/machine/machiner_test.go'
104--- state/apiserver/machine/machiner_test.go 2014-01-20 21:00:43 +0000
105+++ state/apiserver/machine/machiner_test.go 2014-02-11 01:14:25 +0000
106@@ -9,7 +9,6 @@
107 "launchpad.net/juju-core/instance"
108 "launchpad.net/juju-core/state"
109 "launchpad.net/juju-core/state/api/params"
110- "launchpad.net/juju-core/state/apiserver/common"
111 "launchpad.net/juju-core/state/apiserver/machine"
112 apiservertesting "launchpad.net/juju-core/state/apiserver/testing"
113 statetesting "launchpad.net/juju-core/state/testing"
114@@ -17,9 +16,6 @@
115
116 type machinerSuite struct {
117 commonSuite
118-
119- resources *common.Resources
120- machiner *machine.MachinerAPI
121 }
122
123 var _ = gc.Suite(&machinerSuite{})
124@@ -27,18 +23,22 @@
125 func (s *machinerSuite) SetUpTest(c *gc.C) {
126 s.commonSuite.SetUpTest(c)
127
128- // Create the resource registry separately to track invocations to
129- // Register.
130- s.resources = common.NewResources()
131-
132+ // Create a FakeAuthorizer so we can check permissions,
133+ // set up assuming machine 1 has logged in.
134+ s.authorizer = apiservertesting.FakeAuthorizer{
135+ Tag: s.machine1.Tag(),
136+ LoggedIn: true,
137+ MachineAgent: true,
138+ }
139+ var err error
140 // Create a machiner API for machine 1.
141- machiner, err := machine.NewMachinerAPI(
142+ s.machiner, err = machine.NewMachinerAPI(
143 s.State,
144 s.resources,
145 s.authorizer,
146 )
147 c.Assert(err, gc.IsNil)
148- s.machiner = machiner
149+ c.Assert(s.machiner, gc.NotNil)
150 }
151
152 func (s *machinerSuite) TestMachinerFailsWithNonMachineAgentUser(c *gc.C) {
153
154=== added file 'state/apiserver/machine/uniter_test.go'
155--- state/apiserver/machine/uniter_test.go 1970-01-01 00:00:00 +0000
156+++ state/apiserver/machine/uniter_test.go 2014-02-11 01:14:25 +0000
157@@ -0,0 +1,188 @@
158+// Copyright 2013 Canonical Ltd.
159+// Licensed under the AGPLv3, see LICENCE file for details.
160+
161+package machine_test
162+
163+import (
164+ gc "launchpad.net/gocheck"
165+
166+ "launchpad.net/juju-core/instance"
167+ "launchpad.net/juju-core/state"
168+ "launchpad.net/juju-core/state/api/params"
169+ "launchpad.net/juju-core/state/apiserver/machine"
170+ apiservertesting "launchpad.net/juju-core/state/apiserver/testing"
171+ statetesting "launchpad.net/juju-core/state/testing"
172+)
173+
174+// Test that a unit agent has read access to the assigned machine for it's unit.
175+type uniterSuite struct {
176+ commonSuite
177+}
178+
179+var _ = gc.Suite(&uniterSuite{})
180+
181+func (s *uniterSuite) SetUpTest(c *gc.C) {
182+ s.commonSuite.SetUpTest(c)
183+
184+ // Deploy a unit to machine 1.
185+ mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
186+ mysql0, err := mysql.AddUnit()
187+ c.Assert(err, gc.IsNil)
188+ err = mysql0.AssignToMachine(s.machine1)
189+ c.Assert(err, gc.IsNil)
190+ // Create another unit not on machine 1.
191+ _, err = mysql.AddUnit()
192+ c.Assert(err, gc.IsNil)
193+
194+ // Create a FakeAuthorizer so we can check permissions,
195+ // set up assuming mysql/0 has logged in.
196+ s.authorizer = apiservertesting.FakeAuthorizer{
197+ LoggedIn: true,
198+ UnitAgent: true,
199+ Tag: mysql0.Tag(),
200+ Entity: mysql0,
201+ }
202+
203+ // Create a machiner API for mysql/0.
204+ s.machiner, err = machine.NewMachinerAPI(
205+ s.State,
206+ s.resources,
207+ s.authorizer,
208+ )
209+ c.Assert(err, gc.IsNil)
210+ c.Assert(s.machiner, gc.NotNil)
211+}
212+
213+func (s *uniterSuite) TestMachinerFailsWithNonUnitAgentUser(c *gc.C) {
214+ anAuthorizer := s.authorizer
215+ anAuthorizer.UnitAgent = false
216+ aMachiner, err := machine.NewMachinerAPI(s.State, s.resources, anAuthorizer)
217+ c.Assert(err, gc.NotNil)
218+ c.Assert(aMachiner, gc.IsNil)
219+ c.Assert(err, gc.ErrorMatches, "permission denied")
220+}
221+
222+func (s *uniterSuite) TestSetStatusFails(c *gc.C) {
223+ err := s.machine1.SetStatus(params.StatusStarted, "foo", nil)
224+ c.Assert(err, gc.IsNil)
225+
226+ args := params.SetStatus{
227+ Entities: []params.SetEntityStatus{
228+ {Tag: "machine-1", Status: params.StatusError, Info: "not really"},
229+ }}
230+ result, err := s.machiner.SetStatus(args)
231+ c.Assert(err, gc.IsNil)
232+ c.Assert(result, gc.DeepEquals, params.ErrorResults{
233+ Results: []params.ErrorResult{
234+ {apiservertesting.ErrUnauthorized},
235+ },
236+ })
237+
238+ // Verify machine 1 - no change.
239+ status, info, _, err := s.machine1.Status()
240+ c.Assert(err, gc.IsNil)
241+ c.Assert(status, gc.Equals, params.StatusStarted)
242+ c.Assert(info, gc.Equals, "foo")
243+}
244+
245+func (s *uniterSuite) TestLife(c *gc.C) {
246+ c.Assert(s.machine1.Life(), gc.Equals, state.Alive)
247+
248+ args := params.Entities{Entities: []params.Entity{
249+ {Tag: "machine-1"},
250+ {Tag: "machine-0"},
251+ {Tag: "machine-42"},
252+ }}
253+ result, err := s.machiner.Life(args)
254+ c.Assert(err, gc.IsNil)
255+ c.Assert(result, gc.DeepEquals, params.LifeResults{
256+ Results: []params.LifeResult{
257+ {Life: "alive"},
258+ {Error: apiservertesting.ErrUnauthorized},
259+ {Error: apiservertesting.ErrUnauthorized},
260+ },
261+ })
262+}
263+
264+func (s *uniterSuite) TestEnsureDeadFails(c *gc.C) {
265+ c.Assert(s.machine1.Life(), gc.Equals, state.Alive)
266+
267+ args := params.Entities{Entities: []params.Entity{
268+ {Tag: "machine-0"},
269+ {Tag: "machine-1"},
270+ {Tag: "machine-42"},
271+ }}
272+ result, err := s.machiner.EnsureDead(args)
273+ c.Assert(err, gc.IsNil)
274+ c.Assert(result, gc.DeepEquals, params.ErrorResults{
275+ Results: []params.ErrorResult{
276+ {apiservertesting.ErrUnauthorized},
277+ {apiservertesting.ErrUnauthorized},
278+ {apiservertesting.ErrUnauthorized},
279+ },
280+ })
281+
282+ err = s.machine1.Refresh()
283+ c.Assert(err, gc.IsNil)
284+ c.Assert(s.machine1.Life(), gc.Equals, state.Alive)
285+}
286+
287+func (s *uniterSuite) TestSetMachineAddresses(c *gc.C) {
288+ c.Assert(s.machine0.Addresses(), gc.HasLen, 0)
289+ c.Assert(s.machine1.Addresses(), gc.HasLen, 0)
290+
291+ addresses := []instance.Address{
292+ instance.NewAddress("127.0.0.1"),
293+ instance.NewAddress("8.8.8.8"),
294+ }
295+
296+ args := params.SetMachinesAddresses{MachineAddresses: []params.MachineAddresses{
297+ {Tag: "machine-0", Addresses: addresses},
298+ {Tag: "machine-1", Addresses: addresses},
299+ {Tag: "machine-42", Addresses: addresses},
300+ }}
301+
302+ result, err := s.machiner.SetMachineAddresses(args)
303+ c.Assert(err, gc.IsNil)
304+ c.Assert(result, gc.DeepEquals, params.ErrorResults{
305+ Results: []params.ErrorResult{
306+ {apiservertesting.ErrUnauthorized},
307+ {apiservertesting.ErrUnauthorized},
308+ {apiservertesting.ErrUnauthorized},
309+ },
310+ })
311+
312+ err = s.machine1.Refresh()
313+ c.Assert(err, gc.IsNil)
314+ c.Assert(s.machine1.MachineAddresses(), gc.HasLen, 0)
315+}
316+
317+func (s *uniterSuite) TestWatch(c *gc.C) {
318+ c.Assert(s.resources.Count(), gc.Equals, 0)
319+
320+ args := params.Entities{Entities: []params.Entity{
321+ {Tag: "machine-1"},
322+ {Tag: "machine-0"},
323+ {Tag: "machine-42"},
324+ }}
325+ result, err := s.machiner.Watch(args)
326+ c.Assert(err, gc.IsNil)
327+ c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
328+ Results: []params.NotifyWatchResult{
329+ {NotifyWatcherId: "1"},
330+ {Error: apiservertesting.ErrUnauthorized},
331+ {Error: apiservertesting.ErrUnauthorized},
332+ },
333+ })
334+
335+ // Verify the resource was registered and stop when done
336+ c.Assert(s.resources.Count(), gc.Equals, 1)
337+ c.Assert(result.Results[0].NotifyWatcherId, gc.Equals, "1")
338+ resource := s.resources.Get("1")
339+ defer statetesting.AssertStop(c, resource)
340+
341+ // Check that the Watch has consumed the initial event ("returned" in
342+ // the Watch call)
343+ wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
344+ wc.AssertNoChange()
345+}

Subscribers

People subscribed via source and target branches

to status/vote changes: