github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/pinger_test.go (about) 1 // Copyright 2012-2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/api" 17 "github.com/juju/juju/apiserver" 18 "github.com/juju/juju/rpc" 19 coretesting "github.com/juju/juju/testing" 20 ) 21 22 // pingerSuite exercises the apiserver's ping timeout functionality 23 // from the outside. Ping API requests are made (or not) to a running 24 // API server to ensure that the server shuts down the API connection 25 // as expected once there's been no pings within the timeout period. 26 type pingerSuite struct { 27 apiserverBaseSuite 28 } 29 30 var _ = gc.Suite(&pingerSuite{}) 31 32 func (s *pingerSuite) newServerWithTestClock(c *gc.C) (*apiserver.Server, *testing.Clock) { 33 clock := testing.NewClock(time.Now()) 34 config := s.sampleConfig(c) 35 config.PingClock = clock 36 server := s.newServer(c, config) 37 return server, clock 38 } 39 40 func (s *pingerSuite) TestConnectionBrokenDetection(c *gc.C) { 41 server, clock := s.newServerWithTestClock(c) 42 conn, _ := s.OpenAPIAsNewMachine(c, server) 43 44 clock.Advance(api.PingPeriod) 45 // Connection still alive 46 select { 47 case <-conn.Broken(): 48 c.Fatalf("connection should be alive still") 49 case <-time.After(coretesting.ShortWait): 50 // all good, connection still there 51 } 52 53 conn.Close() 54 55 clock.Advance(api.PingPeriod + time.Second) 56 // Check it's detected 57 select { 58 case <-time.After(coretesting.ShortWait): 59 c.Fatalf("connection not closed as expected") 60 case <-conn.Broken(): 61 return 62 } 63 } 64 65 func (s *pingerSuite) TestPing(c *gc.C) { 66 tw := &loggo.TestWriter{} 67 c.Assert(loggo.RegisterWriter("ping-tester", tw), gc.IsNil) 68 69 server, _ := s.newServerWithTestClock(c) 70 conn, _ := s.OpenAPIAsNewMachine(c, server) 71 72 c.Assert(pingConn(conn), jc.ErrorIsNil) 73 c.Assert(conn.Close(), jc.ErrorIsNil) 74 c.Assert(errors.Cause(pingConn(conn)), gc.Equals, rpc.ErrShutdown) 75 76 // Make sure that ping messages have not been logged. 77 for _, m := range tw.Log() { 78 c.Logf("checking %q", m.Message) 79 c.Check(m.Message, gc.Not(gc.Matches), `.*"Request":"Ping".*`) 80 } 81 } 82 83 func (s *pingerSuite) TestClientNoNeedToPing(c *gc.C) { 84 server, clock := s.newServerWithTestClock(c) 85 conn := s.OpenAPIAsAdmin(c, server) 86 87 // Here we have a conundrum, we can't wait for a clock alarm because 88 // one isn't set because we don't have pingers for clients. So just 89 // a short wait then. 90 time.Sleep(coretesting.ShortWait) 91 92 clock.Advance(apiserver.MaxClientPingInterval * 2) 93 c.Assert(pingConn(conn), jc.ErrorIsNil) 94 } 95 96 func (s *pingerSuite) TestAgentConnectionShutsDownWithNoPing(c *gc.C) { 97 server, clock := s.newServerWithTestClock(c) 98 conn, _ := s.OpenAPIAsNewMachine(c, server) 99 100 waitAndAdvance(c, clock, apiserver.MaxClientPingInterval) 101 checkConnectionDies(c, conn) 102 } 103 104 func (s *pingerSuite) TestAgentConnectionDelaysShutdownWithPing(c *gc.C) { 105 server, clock := s.newServerWithTestClock(c) 106 conn, _ := s.OpenAPIAsNewMachine(c, server) 107 108 // As long as we don't wait too long, the connection stays open 109 attemptDelay := apiserver.MaxClientPingInterval / 2 110 for i := 0; i < 10; i++ { 111 waitAndAdvance(c, clock, attemptDelay) 112 c.Assert(pingConn(conn), jc.ErrorIsNil) 113 } 114 115 // However, once we stop pinging for too long, the connection dies 116 waitAndAdvance(c, clock, apiserver.MaxClientPingInterval) 117 checkConnectionDies(c, conn) 118 } 119 120 func (s *pingerSuite) TestAgentConnectionsShutDownWhenAPIServerDies(c *gc.C) { 121 clock := testing.NewClock(time.Now()) 122 config := s.sampleConfig(c) 123 config.Clock = clock 124 server := s.newServerDirtyKill(c, config) 125 conn, _ := s.OpenAPIAsNewMachine(c, server) 126 127 err := pingConn(conn) 128 c.Assert(err, jc.ErrorIsNil) 129 server.Kill() 130 131 // We know this is less than the client ping interval. 132 clock.Advance(apiserver.MongoPingInterval) 133 checkConnectionDies(c, conn) 134 } 135 136 func waitAndAdvance(c *gc.C, clock *testing.Clock, delta time.Duration) { 137 waitForClock(c, clock) 138 clock.Advance(delta) 139 } 140 141 func waitForClock(c *gc.C, clock *testing.Clock) { 142 select { 143 case <-clock.Alarms(): 144 case <-time.After(coretesting.LongWait): 145 c.Fatal("timed out waiting for clock") 146 } 147 } 148 149 func checkConnectionDies(c *gc.C, conn api.Connection) { 150 attempt := utils.AttemptStrategy{ 151 Total: coretesting.LongWait, 152 Delay: coretesting.ShortWait, 153 } 154 for a := attempt.Start(); a.Next(); { 155 err := pingConn(conn) 156 if err != nil { 157 c.Assert(err, gc.ErrorMatches, "connection is shut down") 158 return 159 } 160 } 161 c.Fatal("connection didn't get shut down") 162 } 163 164 func pingConn(conn api.Connection) error { 165 version := conn.BestFacadeVersion("Pinger") 166 return conn.APICall("Pinger", version, "", "Ping", nil, nil) 167 }