Merge lp:~dimitern/juju-core/066-apiserver-stringswatcher into lp:~go-bot/juju-core/trunk

Proposed by Dimiter Naydenov
Status: Merged
Approved by: Dimiter Naydenov
Approved revision: no longer in the source branch.
Merged at revision: 1417
Proposed branch: lp:~dimitern/juju-core/066-apiserver-stringswatcher
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 358 lines (+75/-68)
9 files modified
state/api/interfaces.go (+20/-0)
state/api/params/internal.go (+7/-24)
state/api/watcher/watcher.go (+18/-13)
state/apiserver/root.go (+5/-6)
state/apiserver/watcher.go (+11/-11)
state/watcher.go (+7/-7)
worker/cleaner/cleaner.go (+2/-2)
worker/notifyworker.go (+3/-3)
worker/notifyworker_test.go (+2/-2)
To merge this branch: bzr merge lp:~dimitern/juju-core/066-apiserver-stringswatcher
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+173896@code.launchpad.net

Commit message

state/apiserver: StringsWatcher common impl.

Replaced srvLifecycleWatcher with srvStringsWatcher.
This will be the server-side implementation for any
watcher that returns a list of strings, like the
LifecycleWatcher, MinUnitsWatcher, UnitsWatcher, or
MachineUnitsWatcher.

At client side of the API there will be a single
StringsWatcher implementation of all these watchers,
thus greatly reducing code duplication (as in state).

Also, CleanupWatcher was made conformant with NotifyWatcher,
since it's the same interface. Workers were updated to
reflect the changes to the interface and a new api/interface.go
was introduced to keep the watcher's interfaces there.

https://codereview.appspot.com/10944044/

R=fwereade, fwereade, jameinel, jameinel

Description of the change

state/apiserver: StringsWatcher common impl.

Replaced srvLifecycleWatcher with srvStringsWatcher.
This will be the server-side implementation for any
watcher that returns a list of strings, like the
LifecycleWatcher, MinUnitsWatcher, UnitsWatcher, or
MachineUnitsWatcher.

At client side of the API there will be a single
StringsWatcher implementation of all these watchers,
thus greatly reducing code duplication (as in state).

Also, CleanupWatcher was made conformant with NotifyWatcher,
since it's the same interface. Workers were updated to
reflect the changes to the interface and a new api/interface.go
was introduced to keep the watcher's interfaces there.

https://codereview.appspot.com/10944044/

To post a comment you must log in.
Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Download full text (6.3 KiB)

Reviewers: mp+173896_code.launchpad.net,

Message:
Please take a look.

Description:
state/apiserver: StringsWatcher common impl.

Replaced srvLifecycleWatcher with srvStringsWatcher.
This will be the server-side implementation for any
watcher that returns a list of strings, like the
LifecycleWatcher, MinUnitsWatcher, UnitsWatcher, or
MachineUnitsWatcher.

At client side of the API there will be concrete
implementations of each watcher still.

https://code.launchpad.net/~dimitern/juju-core/066-apiserver-stringswatcher/+merge/173896

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M state/api/params/internal.go
   M state/api/watcher/watcher.go
   M state/apiserver/root.go
   M state/apiserver/watcher.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: tarmac-20130709173747-vea1m7nxrla81n4o
+New revision:
<email address hidden>

Index: state/apiserver/root.go
=== modified file 'state/apiserver/root.go'
--- state/apiserver/root.go 2013-07-09 16:29:17 +0000
+++ state/apiserver/root.go 2013-07-10 09:49:15 +0000
@@ -110,11 +110,10 @@
   }, nil
  }

-// LifecycleWatcher returns an object that provides
-// API access to methods on a state.LifecycleWatcher.
-// Each client has its own current set of watchers, stored
-// in r.resources.
-func (r *srvRoot) LifecycleWatcher(id string) (*srvLifecycleWatcher,
error) {
+// StringsWatcher returns an object that provides API access to
+// methods on a state.StringsWatcher. Each client has its own
+// current set of watchers, stored in r.resources.
+func (r *srvRoot) StringsWatcher(id string) (*srvStringsWatcher, error) {
   if err := r.requireAgent(); err != nil {
    return nil, err
   }
@@ -122,7 +121,7 @@
   if !ok {
    return nil, common.ErrUnknownWatcher
   }
- return &srvLifecycleWatcher{
+ return &srvStringsWatcher{
    watcher: watcher,
    id: id,
    resources: r.resources,

Index: state/apiserver/watcher.go
=== modified file 'state/apiserver/watcher.go'
--- state/apiserver/watcher.go 2013-07-09 16:29:17 +0000
+++ state/apiserver/watcher.go 2013-07-10 09:49:15 +0000
@@ -52,31 +52,31 @@
   return w.resources.Stop(w.id)
  }

-// srvLifecycleWatcher notifies about lifecycle changes for all
-// entities of a given kind. See state.lifecycleWatcher.
-type srvLifecycleWatcher struct {
+// srvStringsWatcher notifies about changes for all entities of a
+// given kind, sending the changes as a list of strings.
+type srvStringsWatcher struct {
   watcher state.StringsWatcher
   id string
   resources *common.Resources
  }

-// Next returns when a change has occured to the lifecycle of an
-// entity of the collection being watched since the most recent call
-// to Next or the Watch call that created the srvLifecycleWatcher.
-func (w *srvLifecycleWatcher) Next() (params.LifecycleWatchResults, error)
{
+// Next returns when a change has occured to an entity of the
+// collection being watched since...

Read more...

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

LGTM, but one quibble re description: I don't mind the client side
having distinct types so long as they're clearly thought through (ie
there's no justification for making client types that mirror the
unexported concrete types backing them in state).

So UnitsLifecycleWatcher would be ok, or StringsWatcher would be ok, but
LifecycleWatcher I think would not. Either way, there should only be one
concrete client-side type for StringsWatcher, there's no point
duplicating all that fiddly logic.

https://codereview.appspot.com/10944044/

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

LGTM

This looks like progress to me, so even if it isn't perfect better to
land and iterate than stall.

https://codereview.appspot.com/10944044/diff/1/state/api/watcher/watcher.go
File state/api/watcher/watcher.go (left):

https://codereview.appspot.com/10944044/diff/1/state/api/watcher/watcher.go#oldcode193
state/api/watcher/watcher.go:193: func (w *LifecycleWatcher) loop()
error {
I would like to think that this LifecycleWatcher would end up being just
a StringsWatcher as well.

https://codereview.appspot.com/10944044/

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

Please take a look.

https://codereview.appspot.com/10944044/diff/1/state/api/watcher/watcher.go
File state/api/watcher/watcher.go (left):

https://codereview.appspot.com/10944044/diff/1/state/api/watcher/watcher.go#oldcode193
state/api/watcher/watcher.go:193: func (w *LifecycleWatcher) loop()
error {
On 2013/07/10 10:11:39, jameinel wrote:
> I would like to think that this LifecycleWatcher would end up being
just a
> StringsWatcher as well.

Done.

https://codereview.appspot.com/10944044/

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

I'm a little unsure of what should be in 'params' and what should be in
api. I personally *like* api.NotifyWatcher better than
params.NotifyWatcher.

I think I was running into problems with circular imports, but maybe
that has been sorted out.

LGTM

https://codereview.appspot.com/10944044/diff/6001/state/api/interface.go
File state/api/interface.go (right):

https://codereview.appspot.com/10944044/diff/6001/state/api/interface.go#newcode3
state/api/interface.go:3:
The other packages seem to call this file "interfaces.go".

tools/interfaces.go
state/apiserver/common/interfaces.go
state/api/common/interfaces.go
environs/interface.go ***

I'd probably go with interfaces (plural) myself, as there is more than
one interface here, right?

(It isn't the interface *of* the API, it is the interfaces *in* the API)

https://codereview.appspot.com/10944044/

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

LGTM, just one note: the workers that were using params.*Watcher should
probably really be using the state.*Watcher interface instead, because
they are after all still implemented in terms of state. Not really
bothered, though, it'll all come out the same in the end.

https://codereview.appspot.com/10944044/

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

https://codereview.appspot.com/10944044/diff/6001/state/api/interface.go
File state/api/interface.go (right):

https://codereview.appspot.com/10944044/diff/6001/state/api/interface.go#newcode3
state/api/interface.go:3:
On 2013/07/10 10:52:13, jameinel wrote:
> The other packages seem to call this file "interfaces.go".

> tools/interfaces.go
> state/apiserver/common/interfaces.go
> state/api/common/interfaces.go
> environs/interface.go ***

> I'd probably go with interfaces (plural) myself, as there is more than
one
> interface here, right?

> (It isn't the interface *of* the API, it is the interfaces *in* the
API)

Renamed.

https://codereview.appspot.com/10944044/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Revision history for this message
Go Bot (go-bot) wrote :

No approved revision specified.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'state/api/interfaces.go'
2--- state/api/interfaces.go 1970-01-01 00:00:00 +0000
3+++ state/api/interfaces.go 2013-07-10 11:15:34 +0000
4@@ -0,0 +1,20 @@
5+// Copyright 2012, 2013 Canonical Ltd.
6+// Licensed under the AGPLv3, see LICENCE file for details.
7+
8+package api
9+
10+// NotifyWatcher will send events when something changes.
11+// It does not send content for those changes.
12+type NotifyWatcher interface {
13+ Changes() <-chan struct{}
14+ Stop() error
15+ Err() error
16+}
17+
18+// StringsWatcher will send events when something changes.
19+// The content for the changes is a list of strings.
20+type StringsWatcher interface {
21+ Changes() <-chan []string
22+ Stop() error
23+ Err() error
24+}
25
26=== modified file 'state/api/params/internal.go'
27--- state/api/params/internal.go 2013-07-08 21:49:45 +0000
28+++ state/api/params/internal.go 2013-07-10 11:15:34 +0000
29@@ -103,15 +103,7 @@
30 Password string
31 }
32
33-// A NotifyWatcher will send events when something changes.
34-// It does not send content for those changes.
35-type NotifyWatcher interface {
36- Changes() <-chan struct{}
37- Stop() error
38- Err() error
39-}
40-
41-// NotifyWatchResult holds an NotifyWatcher id and an error (if any).
42+// NotifyWatchResult holds a NotifyWatcher id and an error (if any).
43 type NotifyWatchResult struct {
44 NotifyWatcherId string
45 Error *Error
46@@ -123,19 +115,10 @@
47 Results []NotifyWatchResult
48 }
49
50-// LifecycleWatchResults holds the results of API calls
51-// that watch the lifecycle of a set of objects.
52-// It is used both for the initial Watch request
53-// and for subsequent Next requests.
54-type LifecycleWatchResults struct {
55- // LifeCycleWatcherId holds the id of the newly
56- // created watcher. It will be empty for a Next
57- // request.
58- LifecycleWatcherId string
59-
60- // Ids holds the list of entity ids.
61- // For a Watch request, it holds all entity ids being
62- // watched; for a Next request, it holds the ids of those
63- // that have changed.
64- Ids []string
65+// StringsWatchResult holds a StringsWatcher id, changes and an error
66+// (if any).
67+type StringsWatchResult struct {
68+ StringsWatcherId string
69+ Changes []string
70+ Error *Error
71 }
72
73=== modified file 'state/api/watcher/watcher.go'
74--- state/api/watcher/watcher.go 2013-07-08 10:44:10 +0000
75+++ state/api/watcher/watcher.go 2013-07-10 11:15:34 +0000
76@@ -5,6 +5,7 @@
77
78 import (
79 "launchpad.net/juju-core/log"
80+ "launchpad.net/juju-core/state/api"
81 "launchpad.net/juju-core/state/api/common"
82 "launchpad.net/juju-core/state/api/params"
83 "launchpad.net/tomb"
84@@ -108,6 +109,8 @@
85 return w.tomb.Err()
86 }
87
88+// notifyWatcher will send events when something changes.
89+// It does not send content for those changes.
90 type notifyWatcher struct {
91 commonWatcher
92 caller common.Caller
93@@ -117,7 +120,7 @@
94
95 // If an API call returns a NotifyWatchResult, you can use this to turn it into
96 // a local Watcher.
97-func NewNotifyWatcher(caller common.Caller, result params.NotifyWatchResult) params.NotifyWatcher {
98+func NewNotifyWatcher(caller common.Caller, result params.NotifyWatchResult) api.NotifyWatcher {
99 w := &notifyWatcher{
100 caller: caller,
101 notifyWatcherId: result.NotifyWatcherId,
102@@ -169,15 +172,17 @@
103 return w.out
104 }
105
106-type LifecycleWatcher struct {
107+// stringsWatcher will send events when something changes.
108+// The content of the changes is a list of strings.
109+type stringsWatcher struct {
110 commonWatcher
111 caller common.Caller
112 watchCall string
113 out chan []string
114 }
115
116-func newLifecycleWatcher(caller common.Caller, watchCall string) *LifecycleWatcher {
117- w := &LifecycleWatcher{
118+func NewStringsWatcher(caller common.Caller, watchCall string) api.StringsWatcher {
119+ w := &stringsWatcher{
120 caller: caller,
121 watchCall: watchCall,
122 out: make(chan []string),
123@@ -190,15 +195,15 @@
124 return w
125 }
126
127-func (w *LifecycleWatcher) loop() error {
128- var result params.LifecycleWatchResults
129+func (w *stringsWatcher) loop() error {
130+ var result params.StringsWatchResult
131 if err := w.caller.Call("State", "", w.watchCall, nil, &result); err != nil {
132 return err
133 }
134- changes := result.Ids
135- w.newResult = func() interface{} { return new(params.LifecycleWatchResults) }
136+ changes := result.Changes
137+ w.newResult = func() interface{} { return new(params.StringsWatchResult) }
138 w.call = func(request string, newResult interface{}) error {
139- return w.caller.Call("LifecycleWatcher", result.LifecycleWatcherId, request, nil, newResult)
140+ return w.caller.Call("StringsWatcher", result.StringsWatcherId, request, nil, newResult)
141 }
142 w.commonWatcher.init()
143 go w.commonLoop()
144@@ -215,7 +220,7 @@
145 return nil
146 }
147 // We have received changes, so send them out.
148- changes = data.(*params.LifecycleWatchResults).Ids
149+ changes = data.(*params.StringsWatchResult).Changes
150 out = w.out
151 case out <- changes:
152 // Wait until we have new changes to send.
153@@ -225,8 +230,8 @@
154 panic("unreachable")
155 }
156
157-// Changes returns a channel that receives a list of ids of watched
158-// entites whose lifecycle has changed.
159-func (w *LifecycleWatcher) Changes() <-chan []string {
160+// Changes returns a channel that receives a list of strings of watched
161+// entites with changes.
162+func (w *stringsWatcher) Changes() <-chan []string {
163 return w.out
164 }
165
166=== modified file 'state/apiserver/root.go'
167--- state/apiserver/root.go 2013-07-09 16:29:17 +0000
168+++ state/apiserver/root.go 2013-07-10 11:15:34 +0000
169@@ -110,11 +110,10 @@
170 }, nil
171 }
172
173-// LifecycleWatcher returns an object that provides
174-// API access to methods on a state.LifecycleWatcher.
175-// Each client has its own current set of watchers, stored
176-// in r.resources.
177-func (r *srvRoot) LifecycleWatcher(id string) (*srvLifecycleWatcher, error) {
178+// StringsWatcher returns an object that provides API access to
179+// methods on a state.StringsWatcher. Each client has its own
180+// current set of watchers, stored in r.resources.
181+func (r *srvRoot) StringsWatcher(id string) (*srvStringsWatcher, error) {
182 if err := r.requireAgent(); err != nil {
183 return nil, err
184 }
185@@ -122,7 +121,7 @@
186 if !ok {
187 return nil, common.ErrUnknownWatcher
188 }
189- return &srvLifecycleWatcher{
190+ return &srvStringsWatcher{
191 watcher: watcher,
192 id: id,
193 resources: r.resources,
194
195=== modified file 'state/apiserver/watcher.go'
196--- state/apiserver/watcher.go 2013-07-09 16:29:17 +0000
197+++ state/apiserver/watcher.go 2013-07-10 11:15:34 +0000
198@@ -52,31 +52,31 @@
199 return w.resources.Stop(w.id)
200 }
201
202-// srvLifecycleWatcher notifies about lifecycle changes for all
203-// entities of a given kind. See state.lifecycleWatcher.
204-type srvLifecycleWatcher struct {
205+// srvStringsWatcher notifies about changes for all entities of a
206+// given kind, sending the changes as a list of strings.
207+type srvStringsWatcher struct {
208 watcher state.StringsWatcher
209 id string
210 resources *common.Resources
211 }
212
213-// Next returns when a change has occured to the lifecycle of an
214-// entity of the collection being watched since the most recent call
215-// to Next or the Watch call that created the srvLifecycleWatcher.
216-func (w *srvLifecycleWatcher) Next() (params.LifecycleWatchResults, error) {
217+// Next returns when a change has occured to an entity of the
218+// collection being watched since the most recent call to Next
219+// or the Watch call that created the srvStringsWatcher.
220+func (w *srvStringsWatcher) Next() (params.StringsWatchResult, error) {
221 if changes, ok := <-w.watcher.Changes(); ok {
222- return params.LifecycleWatchResults{
223- Ids: changes,
224+ return params.StringsWatchResult{
225+ Changes: changes,
226 }, nil
227 }
228 err := w.watcher.Err()
229 if err == nil {
230 err = common.ErrStoppedWatcher
231 }
232- return params.LifecycleWatchResults{}, err
233+ return params.StringsWatchResult{}, err
234 }
235
236 // Stop stops the watcher.
237-func (w *srvLifecycleWatcher) Stop() error {
238+func (w *srvStringsWatcher) Stop() error {
239 return w.resources.Stop(w.id)
240 }
241
242=== modified file 'state/watcher.go'
243--- state/watcher.go 2013-07-09 16:29:17 +0000
244+++ state/watcher.go 2013-07-10 11:15:34 +0000
245@@ -1226,19 +1226,19 @@
246 panic("unreachable")
247 }
248
249-// CleanupWatcher notifies of changes in the cleanups collection.
250-type CleanupWatcher struct {
251+// cleanupWatcher notifies of changes in the cleanups collection.
252+type cleanupWatcher struct {
253 commonWatcher
254 out chan struct{}
255 }
256
257 // WatchCleanups starts and returns a CleanupWatcher.
258-func (st *State) WatchCleanups() *CleanupWatcher {
259+func (st *State) WatchCleanups() NotifyWatcher {
260 return newCleanupWatcher(st)
261 }
262
263-func newCleanupWatcher(st *State) *CleanupWatcher {
264- w := &CleanupWatcher{
265+func newCleanupWatcher(st *State) NotifyWatcher {
266+ w := &cleanupWatcher{
267 commonWatcher: commonWatcher{st: st},
268 out: make(chan struct{}),
269 }
270@@ -1251,11 +1251,11 @@
271 }
272
273 // Changes returns the event channel for w.
274-func (w *CleanupWatcher) Changes() <-chan struct{} {
275+func (w *cleanupWatcher) Changes() <-chan struct{} {
276 return w.out
277 }
278
279-func (w *CleanupWatcher) loop() (err error) {
280+func (w *cleanupWatcher) loop() (err error) {
281 in := make(chan watcher.Change)
282
283 w.st.watcher.WatchCollection(w.st.cleanups.Name, in)
284
285=== modified file 'worker/cleaner/cleaner.go'
286--- worker/cleaner/cleaner.go 2013-07-08 12:29:48 +0000
287+++ worker/cleaner/cleaner.go 2013-07-10 11:15:34 +0000
288@@ -8,7 +8,7 @@
289
290 "launchpad.net/juju-core/log"
291 "launchpad.net/juju-core/state"
292- "launchpad.net/juju-core/state/api/params"
293+ "launchpad.net/juju-core/state/api"
294 "launchpad.net/juju-core/worker"
295 )
296
297@@ -27,7 +27,7 @@
298 return fmt.Sprintf("cleaner")
299 }
300
301-func (c *Cleaner) SetUp() (params.NotifyWatcher, error) {
302+func (c *Cleaner) SetUp() (api.NotifyWatcher, error) {
303 return c.st.WatchCleanups(), nil
304 }
305
306
307=== modified file 'worker/notifyworker.go'
308--- worker/notifyworker.go 2013-07-08 18:50:58 +0000
309+++ worker/notifyworker.go 2013-07-10 11:15:34 +0000
310@@ -6,7 +6,7 @@
311 import (
312 "launchpad.net/tomb"
313
314- "launchpad.net/juju-core/state/api/params"
315+ "launchpad.net/juju-core/state/api"
316 "launchpad.net/juju-core/state/watcher"
317 )
318
319@@ -47,7 +47,7 @@
320 // SetUp starts the handler, this should create the watcher we will be
321 // waiting on for more events. SetUp can return a Watcher even if there
322 // is an error, and NotifyWorker will make sure to stop the Watcher.
323- SetUp() (params.NotifyWatcher, error)
324+ SetUp() (api.NotifyWatcher, error)
325
326 // TearDown should cleanup any resources that are left around
327 TearDown() error
328@@ -106,7 +106,7 @@
329 }
330
331 func (nw *notifyWorker) loop() error {
332- var w params.NotifyWatcher
333+ var w api.NotifyWatcher
334 var err error
335 defer handlerTearDown(nw.handler, &nw.tomb)
336 if w, err = nw.handler.SetUp(); err != nil {
337
338=== modified file 'worker/notifyworker_test.go'
339--- worker/notifyworker_test.go 2013-07-09 08:40:22 +0000
340+++ worker/notifyworker_test.go 2013-07-10 11:15:34 +0000
341@@ -11,7 +11,7 @@
342 gc "launchpad.net/gocheck"
343 "launchpad.net/tomb"
344
345- "launchpad.net/juju-core/state/api/params"
346+ "launchpad.net/juju-core/state/api"
347 "launchpad.net/juju-core/state/watcher"
348 coretesting "launchpad.net/juju-core/testing"
349 jc "launchpad.net/juju-core/testing/checkers"
350@@ -56,7 +56,7 @@
351 description string
352 }
353
354-func (a *actionsHandler) SetUp() (params.NotifyWatcher, error) {
355+func (a *actionsHandler) SetUp() (api.NotifyWatcher, error) {
356 a.mu.Lock()
357 defer a.mu.Unlock()
358 a.actions = append(a.actions, "setup")

Subscribers

People subscribed via source and target branches

to status/vote changes: