Merge lp:~jameinel/juju-core/api-named-resources into lp:~go-bot/juju-core/trunk

Proposed by John A Meinel
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 2753
Proposed branch: lp:~jameinel/juju-core/api-named-resources
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 241 lines (+116/-13)
6 files modified
state/apiserver/admin.go (+2/-1)
state/apiserver/common/resource.go (+21/-0)
state/apiserver/common/resource_test.go (+50/-0)
state/apiserver/pinger_test.go (+19/-0)
state/apiserver/root.go (+8/-11)
state/apiserver/root_test.go (+16/-1)
To merge this branch: bzr merge lp:~jameinel/juju-core/api-named-resources
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+220208@code.launchpad.net

Commit message

state/apiserver/common: Resources.RegisterNamed

Two small changes here.

1) Change common.Resources to allow you to RegisterNamed rather than
   only allowing you to register and get back the Id. This was necessary
   for the second step, but generally allows us to declare Resources
   that Facades can know about that aren't reported to clients.

2) Change the pingTimeout object from being an attribute of srvRoot into
   being just another resource like the Watchers at a specific resource
   name.

While working on the changes for pingTimeout, I noticed that no tests
actually failed if I started returning nullTimeout. So I added a test
that ensures calling Ping() actually does delay the connection from
being closed. (We only had a test that if you never called Ping in time,
it would die, but not that Ping can delay that death.)

This is a step towards making all of the Facades follow a common
pattern, as this allows Pinger to also just take Resources rather than
needing to know about details of the srvRoot object.

https://codereview.appspot.com/93500044/

Description of the change

state/apiserver/common: Resources.RegisterNamed

Two small changes here.

1) Change common.Resources to allow you to RegisterNamed rather than
   only allowing you to register and get back the Id. This was necessary
   for the second step, but generally allows us to declare Resources
   that Facades can know about that aren't reported to clients.

2) Change the pingTimeout object from being an attribute of srvRoot into
   being just another resource like the Watchers at a specific resource
   name.

While working on the changes for pingTimeout, I noticed that no tests
actually failed if I started returning nullTimeout. So I added a test
that ensures calling Ping() actually does delay the connection from
being closed. (We only had a test that if you never called Ping in time,
it would die, but not that Ping can delay that death.)

This is a step towards making all of the Facades follow a common
pattern, as this allows Pinger to also just take Resources rather than
needing to know about details of the srvRoot object.

https://codereview.appspot.com/93500044/

To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

Reviewers: mp+220208_code.launchpad.net,

Message:
Please take a look.

Description:
state/apiserver/common: Resources.RegisterNamed

Two small changes here.

1) Change common.Resources to allow you to RegisterNamed rather than
    only allowing you to register and get back the Id. This was necessary
    for the second step, but generally allows us to declare Resources
    that Facades can know about that aren't reported to clients.

2) Change the pingTimeout object from being an attribute of srvRoot into
    being just another resource like the Watchers at a specific resource
    name.

While working on the changes for pingTimeout, I noticed that no tests
actually failed if I started returning nullTimeout. So I added a test
that ensures calling Ping() actually does delay the connection from
being closed. (We only had a test that if you never called Ping in time,
it would die, but not that Ping can delay that death.)

This is a step towards making all of the Facades follow a common
pattern, as this allows Pinger to also just take Resources rather than
needing to know about details of the srvRoot object.

https://code.launchpad.net/~jameinel/juju-core/api-named-resources/+merge/220208

(do not edit description out of merge proposal)

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

Affected files (+118, -13 lines):
   A [revision details]
   M state/apiserver/admin.go
   M state/apiserver/common/resource.go
   M state/apiserver/common/resource_test.go
   M state/apiserver/pinger_test.go
   M state/apiserver/root.go
   M state/apiserver/root_test.go

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'state/apiserver/admin.go'
2--- state/apiserver/admin.go 2014-05-13 04:30:48 +0000
3+++ state/apiserver/admin.go 2014-05-20 08:25:48 +0000
4@@ -181,6 +181,7 @@
5 logger.Errorf("error closing the RPC connection: %v", err)
6 }
7 }
8- newRoot.pingTimeout = newPingTimeout(action, maxClientPingInterval)
9+ pingTimeout := newPingTimeout(action, maxClientPingInterval)
10+ newRoot.resources.RegisterNamed("pingTimeout", pingTimeout)
11 return nil
12 }
13
14=== modified file 'state/apiserver/common/resource.go'
15--- state/apiserver/common/resource.go 2014-04-17 01:26:34 +0000
16+++ state/apiserver/common/resource.go 2014-05-20 08:25:48 +0000
17@@ -4,6 +4,7 @@
18 package common
19
20 import (
21+ "fmt"
22 "strconv"
23 "sync"
24 )
25@@ -50,6 +51,26 @@
26 return id
27 }
28
29+// RegisterNamed registers the given resource. Callers must supply a unique
30+// name for the given resource. It is an error to try to register another
31+// resource with the same name as an already registered name. (This could be
32+// softened that you can overwrite an existing one and it will be Stopped and
33+// replaced, but we don't have a need for that yet.)
34+// It is also an error to supply a name that is an integer string, since that
35+// collides with the auto-naming from Register.
36+func (rs *Resources) RegisterNamed(name string, r Resource) error {
37+ rs.mu.Lock()
38+ defer rs.mu.Unlock()
39+ if _, err := strconv.Atoi(name); err == nil {
40+ return fmt.Errorf("RegisterNamed does not allow integer names: %q", name)
41+ }
42+ if _, ok := rs.resources[name]; ok {
43+ return fmt.Errorf("resource %q already registered", name)
44+ }
45+ rs.resources[name] = r
46+ return nil
47+}
48+
49 // Stop stops the resource with the given id and unregisters it.
50 // It returns any error from the underlying Stop call.
51 // It does not return an error if the resource has already
52
53=== modified file 'state/apiserver/common/resource_test.go'
54--- state/apiserver/common/resource_test.go 2013-08-05 10:19:47 +0000
55+++ state/apiserver/common/resource_test.go 2014-05-20 08:25:48 +0000
56@@ -36,6 +36,50 @@
57 c.Assert(rs.Count(), gc.Equals, 2)
58 }
59
60+func (resourceSuite) TestRegisterNamedGetCount(c *gc.C) {
61+ rs := common.NewResources()
62+ defer rs.StopAll()
63+ r1 := &fakeResource{}
64+ err := rs.RegisterNamed("fake1", r1)
65+ c.Assert(err, gc.IsNil)
66+ c.Check(rs.Count(), gc.Equals, 1)
67+ c.Check(rs.Get("fake1"), gc.Equals, r1)
68+}
69+
70+func (resourceSuite) TestRegisterNamedRepeatedName(c *gc.C) {
71+ rs := common.NewResources()
72+ defer rs.StopAll()
73+ r1 := &fakeResource{}
74+ r2 := &fakeResource{}
75+ err := rs.RegisterNamed("fake1", r1)
76+ c.Assert(err, gc.IsNil)
77+ c.Check(rs.Count(), gc.Equals, 1)
78+ err = rs.RegisterNamed("fake1", r2)
79+ c.Check(err, gc.ErrorMatches, `resource "fake1" already registered`)
80+ c.Check(rs.Count(), gc.Equals, 1)
81+ c.Check(rs.Get("fake1"), gc.Equals, r1)
82+}
83+
84+func (resourceSuite) TestRegisterNamedIntegerName(c *gc.C) {
85+ rs := common.NewResources()
86+ defer rs.StopAll()
87+ r1 := &fakeResource{}
88+ err := rs.RegisterNamed("1", r1)
89+ c.Check(err, gc.ErrorMatches, `RegisterNamed does not allow integer names: "1"`)
90+ c.Check(rs.Count(), gc.Equals, 0)
91+ c.Check(rs.Get("fake1"), gc.IsNil)
92+}
93+
94+func (resourceSuite) TestRegisterNamedIntegerStart(c *gc.C) {
95+ rs := common.NewResources()
96+ defer rs.StopAll()
97+ r1 := &fakeResource{}
98+ err := rs.RegisterNamed("1fake", r1)
99+ c.Assert(err, gc.IsNil)
100+ c.Check(rs.Count(), gc.Equals, 1)
101+ c.Check(rs.Get("1fake"), gc.Equals, r1)
102+}
103+
104 func (resourceSuite) TestConcurrency(c *gc.C) {
105 // This test is designed to cause the race detector
106 // to fail if the locking is not done correctly.
107@@ -54,6 +98,9 @@
108 rs.Register(&fakeResource{})
109 })
110 start(func() {
111+ rs.RegisterNamed("named", &fakeResource{})
112+ })
113+ start(func() {
114 rs.Stop("1")
115 })
116 start(func() {
117@@ -65,6 +112,9 @@
118 start(func() {
119 rs.Get("2")
120 })
121+ start(func() {
122+ rs.Get("named")
123+ })
124 wg.Wait()
125 }
126
127
128=== modified file 'state/apiserver/pinger_test.go'
129--- state/apiserver/pinger_test.go 2014-04-04 16:52:41 +0000
130+++ state/apiserver/pinger_test.go 2014-05-20 08:25:48 +0000
131@@ -86,6 +86,25 @@
132 c.Assert(err, gc.ErrorMatches, "connection is shut down")
133 }
134
135+func (s *stateSuite) TestAgentConnectionDelaysShutdownWithPing(c *gc.C) {
136+ // We have to be careful, because Login can take 25ms, so we ping
137+ // immediately after connecting.
138+ s.PatchValue(apiserver.MaxClientPingInterval, 50*time.Millisecond)
139+ st, _ := s.OpenAPIAsNewMachine(c)
140+ err := st.Ping()
141+ c.Assert(err, gc.IsNil)
142+ // As long as we don't wait too long, the connection stays open
143+ for i := 0; i < 10; i++ {
144+ time.Sleep(10 * time.Millisecond)
145+ err = st.Ping()
146+ c.Assert(err, gc.IsNil)
147+ }
148+ // However, once we stop pinging for too long, the connection dies
149+ time.Sleep(75 * time.Millisecond)
150+ err = st.Ping()
151+ c.Assert(err, gc.ErrorMatches, "connection is shut down")
152+}
153+
154 type mongoPingerSuite struct {
155 testing.JujuConnSuite
156 }
157
158=== modified file 'state/apiserver/root.go'
159--- state/apiserver/root.go 2014-05-14 16:07:11 +0000
160+++ state/apiserver/root.go 2014-05-20 08:25:48 +0000
161@@ -56,10 +56,9 @@
162 // after it has logged in.
163 type srvRoot struct {
164 clientAPI
165- srv *Server
166- rpcConn *rpc.Conn
167- resources *common.Resources
168- pingTimeout *pingTimeout
169+ srv *Server
170+ rpcConn *rpc.Conn
171+ resources *common.Resources
172
173 entity taggedAuthenticator
174 }
175@@ -82,9 +81,6 @@
176 // cleaning up to ensure that all outstanding requests return.
177 func (r *srvRoot) Kill() {
178 r.resources.StopAll()
179- if r.pingTimeout != nil {
180- r.pingTimeout.stop()
181- }
182 }
183
184 // requireAgent checks whether the current client is an agent and hence
185@@ -347,10 +343,11 @@
186 // is not called frequently enough, the connection
187 // will be dropped.
188 func (r *srvRoot) Pinger(id string) (pinger, error) {
189- if r.pingTimeout == nil {
190+ pingTimeout, ok := r.resources.Get("pingTimeout").(pinger)
191+ if !ok {
192 return nullPinger{}, nil
193 }
194- return r.pingTimeout, nil
195+ return pingTimeout, nil
196 }
197
198 type nullPinger struct{}
199@@ -438,8 +435,8 @@
200 }
201 }
202
203-// stop terminates the ping timeout.
204-func (pt *pingTimeout) stop() error {
205+// Stop terminates the ping timeout.
206+func (pt *pingTimeout) Stop() error {
207 pt.tomb.Kill(nil)
208 return pt.tomb.Wait()
209 }
210
211=== modified file 'state/apiserver/root_test.go'
212--- state/apiserver/root_test.go 2014-01-22 19:28:08 +0000
213+++ state/apiserver/root_test.go 2014-05-20 08:25:48 +0000
214@@ -58,7 +58,6 @@
215 // Expect action to be executed about 50ms after last ping.
216 broken := time.Now()
217 var closed time.Time
218- time.Sleep(100 * time.Millisecond)
219 select {
220 case closed = <-closedc:
221 case <-time.After(testing.LongWait):
222@@ -67,3 +66,19 @@
223 closeDiff := closed.Sub(broken) / time.Millisecond
224 c.Assert(50 <= closeDiff && closeDiff <= 100, gc.Equals, true)
225 }
226+
227+func (r *rootSuite) TestPingTimeoutStopped(c *gc.C) {
228+ closedc := make(chan time.Time, 1)
229+ action := func() {
230+ closedc <- time.Now()
231+ }
232+ timeout := apiserver.NewPingTimeout(action, 20*time.Millisecond)
233+ timeout.Ping()
234+ timeout.Stop()
235+ // The action should never trigger
236+ select {
237+ case <-closedc:
238+ c.Fatalf("action triggered after Stop()")
239+ case <-time.After(testing.ShortWait):
240+ }
241+}

Subscribers

People subscribed via source and target branches

to status/vote changes: