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