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  }