Merge lp:~chipaca/ubuntu-push/prepersistance into lp:ubuntu-push

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 63
Merged at revision: 63
Proposed branch: lp:~chipaca/ubuntu-push/prepersistance
Merge into: lp:ubuntu-push
Prerequisite: lp:~chipaca/ubuntu-push/client-constructor
Diff against target: 244 lines (+107/-21)
4 files modified
client/session/levelmap/levelmap.go (+8/-7)
client/session/levelmap/levelmap_test.go (+22/-8)
client/session/session.go (+22/-4)
client/session/session_test.go (+55/-2)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/prepersistance
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+205473@code.launchpad.net

Commit message

change the levelmap interface to return errors

Description of the change

In preparation for the persistence branch, change the levelmap interface to (also) return errors.

To post a comment you must log in.
61. By John Lenton

Merged client-constructor into prepersistance.

Revision history for this message
Samuele Pedroni (pedronis) wrote :

109 - err := sess.proto.WriteMessage(protocol.AckMsg{"ack"})
110 + err := sess.Levels.Set(bcast.ChanId, bcast.TopLevel)
111 + if err != nil {
112 + sess.setState(Error)
113 + sess.Log.Errorf("unable to set level: %v", err)
114 + return err
115 + }
116 + // the server assumes if we ack the broadcast, we've updated
117 + // our levels. Hence the order.

the server will wait ExchangeTimeout and then kill the session, quicker to send a nack? it seems a serious problem on the client, not sure how you expect to recover without user intervention?

118 + err = sess.proto.WriteMessage(protocol.AckMsg{"ack"})

Revision history for this message
Samuele Pedroni (pedronis) wrote :

        state := uint32(Disconnected)
        levels, err := levelmap.NewLevelMap()
        if err != nil {
                return nil, err
        }

the error path there is not covered fwiw

Revision history for this message
Samuele Pedroni (pedronis) wrote :

71 + c.Check(err, IsNil)

should this one and similar be Assert?

Revision history for this message
John Lenton (chipaca) wrote :

Yes! yes, it should send a nack. Fixing.

The error path gets covered further down the pipeline, when there is a levelmap that can actually return non-nil error.

I use c.Assert on the error when I need to dereference the result (and in this case I don't). Otherwise I'd rather let it go ahead.

62. By John Lenton

send nack on levelmap set error

63. By John Lenton

nak, not nack

Revision history for this message
Samuele Pedroni (pedronis) wrote :

looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'client/session/levelmap/levelmap.go'
--- client/session/levelmap/levelmap.go 2014-01-30 10:46:21 +0000
+++ client/session/levelmap/levelmap.go 2014-02-12 13:24:35 +0000
@@ -24,23 +24,24 @@
2424
25type LevelMap interface {25type LevelMap interface {
26 // Set() (re)sets the given level to the given value.26 // Set() (re)sets the given level to the given value.
27 Set(level string, top int64)27 Set(level string, top int64) error
28 // GetAll() returns a "simple" map of the current levels.28 // GetAll() returns a "simple" map of the current levels.
29 GetAll() map[string]int6429 GetAll() (map[string]int64, error)
30}30}
3131
32type mapLevelMap map[string]int6432type mapLevelMap map[string]int64
3333
34func (m *mapLevelMap) Set(level string, top int64) {34func (m *mapLevelMap) Set(level string, top int64) error {
35 (*m)[level] = top35 (*m)[level] = top
36 return nil
36}37}
37func (m *mapLevelMap) GetAll() map[string]int64 {38func (m *mapLevelMap) GetAll() (map[string]int64, error) {
38 return map[string]int64(*m)39 return map[string]int64(*m), nil
39}40}
4041
41var _ LevelMap = &mapLevelMap{}42var _ LevelMap = &mapLevelMap{}
4243
43// default constructor44// default constructor
44func NewLevelMap() LevelMap {45func NewLevelMap() (LevelMap, error) {
45 return &mapLevelMap{}46 return &mapLevelMap{}, nil
46}47}
4748
=== modified file 'client/session/levelmap/levelmap_test.go'
--- client/session/levelmap/levelmap_test.go 2014-01-30 10:46:21 +0000
+++ client/session/levelmap/levelmap_test.go 2014-02-12 13:24:35 +0000
@@ -23,19 +23,33 @@
2323
24func TestLevelMap(t *testing.T) { TestingT(t) }24func TestLevelMap(t *testing.T) { TestingT(t) }
2525
26type lmSuite struct{}26type lmSuite struct {
27 constructor func() (LevelMap, error)
28}
2729
28var _ = Suite(&lmSuite{})30var _ = Suite(&lmSuite{})
2931
30func (cs *lmSuite) TestAllTheThings(c *C) {32func (s *lmSuite) SetUpSuite(c *C) {
33 s.constructor = NewLevelMap
34}
35
36func (s *lmSuite) TestAllTheThings(c *C) {
37 var err error
38 var lm LevelMap
31 // checks NewLevelMap returns a LevelMap39 // checks NewLevelMap returns a LevelMap
32 var lm LevelMap = NewLevelMap()40 lm, err = s.constructor()
41 // and that it works
42 c.Assert(err, IsNil)
33 // setting a couple of things, sets them43 // setting a couple of things, sets them
34 lm.Set("this", 12)44 c.Check(lm.Set("this", 12), IsNil)
35 lm.Set("that", 42)45 c.Check(lm.Set("that", 42), IsNil)
36 c.Check(lm.GetAll(), DeepEquals, map[string]int64{"this": 12, "that": 42})46 all, err := lm.GetAll()
47 c.Check(err, IsNil)
48 c.Check(all, DeepEquals, map[string]int64{"this": 12, "that": 42})
37 // re-setting one of them, resets it49 // re-setting one of them, resets it
38 lm.Set("this", 999)50 c.Check(lm.Set("this", 999), IsNil)
39 c.Check(lm.GetAll(), DeepEquals, map[string]int64{"this": 999, "that": 42})51 all, err = lm.GetAll()
52 c.Check(err, IsNil)
53 c.Check(all, DeepEquals, map[string]int64{"this": 999, "that": 42})
40 // huzzah54 // huzzah
41}55}
4256
=== modified file 'client/session/session.go'
--- client/session/session.go 2014-02-05 13:23:53 +0000
+++ client/session/session.go 2014-02-12 13:24:35 +0000
@@ -80,13 +80,17 @@
80func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration,80func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration,
81 deviceId string, log logger.Logger) (*ClientSession, error) {81 deviceId string, log logger.Logger) (*ClientSession, error) {
82 state := uint32(Disconnected)82 state := uint32(Disconnected)
83 levels, err := levelmap.NewLevelMap()
84 if err != nil {
85 return nil, err
86 }
83 sess := &ClientSession{87 sess := &ClientSession{
84 ExchangeTimeout: exchangeTimeout,88 ExchangeTimeout: exchangeTimeout,
85 ServerAddr: serverAddr,89 ServerAddr: serverAddr,
86 DeviceId: deviceId,90 DeviceId: deviceId,
87 Log: log,91 Log: log,
88 Protocolator: protocol.NewProtocol0,92 Protocolator: protocol.NewProtocol0,
89 Levels: levelmap.NewLevelMap(),93 Levels: levels,
90 TLS: &tls.Config{InsecureSkipVerify: true}, // XXX94 TLS: &tls.Config{InsecureSkipVerify: true}, // XXX
91 stateP: &state,95 stateP: &state,
92 }96 }
@@ -164,7 +168,16 @@
164168
165// handle "broadcast" messages169// handle "broadcast" messages
166func (sess *ClientSession) handleBroadcast(bcast *serverMsg) error {170func (sess *ClientSession) handleBroadcast(bcast *serverMsg) error {
167 err := sess.proto.WriteMessage(protocol.AckMsg{"ack"})171 err := sess.Levels.Set(bcast.ChanId, bcast.TopLevel)
172 if err != nil {
173 sess.setState(Error)
174 sess.Log.Errorf("unable to set level: %v", err)
175 sess.proto.WriteMessage(protocol.AckMsg{"nak"})
176 return err
177 }
178 // the server assumes if we ack the broadcast, we've updated
179 // our levels. Hence the order.
180 err = sess.proto.WriteMessage(protocol.AckMsg{"ack"})
168 if err != nil {181 if err != nil {
169 sess.setState(Error)182 sess.setState(Error)
170 sess.Log.Errorf("unable to ack broadcast: %s", err)183 sess.Log.Errorf("unable to ack broadcast: %s", err)
@@ -175,7 +188,6 @@
175 if bcast.ChanId == protocol.SystemChannelId {188 if bcast.ChanId == protocol.SystemChannelId {
176 // the system channel id, the only one we care about for now189 // the system channel id, the only one we care about for now
177 sess.Log.Debugf("sending it over")190 sess.Log.Debugf("sending it over")
178 sess.Levels.Set(bcast.ChanId, bcast.TopLevel)
179 sess.MsgCh <- &Notification{}191 sess.MsgCh <- &Notification{}
180 sess.Log.Debugf("sent it over")192 sess.Log.Debugf("sent it over")
181 } else {193 } else {
@@ -228,10 +240,16 @@
228 }240 }
229 proto := sess.Protocolator(conn)241 proto := sess.Protocolator(conn)
230 proto.SetDeadline(time.Now().Add(sess.ExchangeTimeout))242 proto.SetDeadline(time.Now().Add(sess.ExchangeTimeout))
243 levels, err := sess.Levels.GetAll()
244 if err != nil {
245 sess.setState(Error)
246 sess.Log.Errorf("unable to start: get levels: %v", err)
247 return err
248 }
231 err = proto.WriteMessage(protocol.ConnectMsg{249 err = proto.WriteMessage(protocol.ConnectMsg{
232 Type: "connect",250 Type: "connect",
233 DeviceId: sess.DeviceId,251 DeviceId: sess.DeviceId,
234 Levels: sess.Levels.GetAll(),252 Levels: levels,
235 })253 })
236 if err != nil {254 if err != nil {
237 sess.setState(Error)255 sess.setState(Error)
238256
=== modified file 'client/session/session_test.go'
--- client/session/session_test.go 2014-02-05 18:23:47 +0000
+++ client/session/session_test.go 2014-02-12 13:24:35 +0000
@@ -156,6 +156,14 @@
156 return nil156 return nil
157}157}
158158
159// brokenLevelMap is a LevelMap that always breaks
160type brokenLevelMap struct{}
161
162func (*brokenLevelMap) Set(string, int64) error { return errors.New("broken.") }
163func (*brokenLevelMap) GetAll() (map[string]int64, error) { return nil, errors.New("broken.") }
164
165/////
166
159func (cs *clientSessionSuite) SetUpTest(c *C) {167func (cs *clientSessionSuite) SetUpTest(c *C) {
160 cs.log = helpers.NewTestLogger(c, "debug")168 cs.log = helpers.NewTestLogger(c, "debug")
161}169}
@@ -365,7 +373,9 @@
365 c.Assert(len(s.sess.MsgCh), Equals, 1)373 c.Assert(len(s.sess.MsgCh), Equals, 1)
366 c.Check(<-s.sess.MsgCh, Equals, &Notification{})374 c.Check(<-s.sess.MsgCh, Equals, &Notification{})
367 // and finally, the session keeps track of the levels375 // and finally, the session keeps track of the levels
368 c.Check(s.sess.Levels.GetAll(), DeepEquals, map[string]int64{"0": 2})376 levels, err := s.sess.Levels.GetAll()
377 c.Check(err, IsNil)
378 c.Check(levels, DeepEquals, map[string]int64{"0": 2})
369}379}
370380
371func (s *msgSuite) TestHandleBroadcastBadAckWrite(c *C) {381func (s *msgSuite) TestHandleBroadcastBadAckWrite(c *C) {
@@ -401,6 +411,27 @@
401 c.Check(len(s.sess.MsgCh), Equals, 0)411 c.Check(len(s.sess.MsgCh), Equals, 0)
402}412}
403413
414func (s *msgSuite) TestHandleBroadcastWrongBrokenLevelmap(c *C) {
415 s.sess.Levels = &brokenLevelMap{}
416 msg := serverMsg{"broadcast",
417 protocol.BroadcastMsg{
418 Type: "broadcast",
419 AppId: "--ignored--",
420 ChanId: "0",
421 TopLevel: 2,
422 Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)},
423 }, protocol.NotificationsMsg{}}
424 go func() { s.errCh <- s.sess.handleBroadcast(&msg) }()
425 s.upCh <- nil // ack ok
426 // start returns with error
427 c.Check(<-s.errCh, Not(Equals), nil)
428 // no message sent out
429 c.Check(len(s.sess.MsgCh), Equals, 0)
430 // and nak'ed it
431 c.Check(len(s.downCh), Equals, 1)
432 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"nak"})
433}
434
404/****************************************************************435/****************************************************************
405 loop() tests436 loop() tests
406****************************************************************/437****************************************************************/
@@ -488,6 +519,26 @@
488 c.Check(sess.State(), Equals, Error)519 c.Check(sess.State(), Equals, Error)
489}520}
490521
522func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) {
523 sess, err := NewSession("", nil, 0, "wah", cs.log)
524 c.Assert(err, IsNil)
525 sess.Levels = &brokenLevelMap{}
526 sess.Connection = &testConn{Name: "TestStartConnectMessageFails"}
527 errCh := make(chan error, 1)
528 upCh := make(chan interface{}, 5)
529 downCh := make(chan interface{}, 5)
530 proto := &testProtocol{up: upCh, down: downCh}
531 sess.Protocolator = func(_ net.Conn) protocol.Protocol { return proto }
532
533 go func() {
534 errCh <- sess.start()
535 }()
536
537 c.Check(takeNext(downCh), Equals, "deadline 0")
538 err = <-errCh
539 c.Check(err, ErrorMatches, "broken.")
540}
541
491func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) {542func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) {
492 sess, err := NewSession("", nil, 0, "wah", cs.log)543 sess, err := NewSession("", nil, 0, "wah", cs.log)
493 c.Assert(err, IsNil)544 c.Assert(err, IsNil)
@@ -788,7 +839,9 @@
788 // ...get bubbled up,839 // ...get bubbled up,
789 c.Check(<-sess.MsgCh, NotNil)840 c.Check(<-sess.MsgCh, NotNil)
790 // and their TopLevel remembered841 // and their TopLevel remembered
791 c.Check(sess.Levels.GetAll(), DeepEquals, map[string]int64{"0": 2})842 levels, err := sess.Levels.GetAll()
843 c.Check(err, IsNil)
844 c.Check(levels, DeepEquals, map[string]int64{"0": 2})
792845
793 // and ping still work even after that.846 // and ping still work even after that.
794 c.Check(takeNext(downCh), Equals, "deadline 110ms")847 c.Check(takeNext(downCh), Equals, "deadline 110ms")

Subscribers

People subscribed via source and target branches