github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/pinger_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // TODO(wallyworld) bug http://pad.lv/1408459
     5  // Re-enable tests for i386 when these tests are fixed to work on that architecture.
     6  
     7  // +build !386
     8  
     9  package apiserver_test
    10  
    11  import (
    12  	"time"
    13  
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  	gitjujutesting "github.com/juju/testing"
    17  	jc "github.com/juju/testing/checkers"
    18  	"github.com/juju/utils"
    19  	gc "gopkg.in/check.v1"
    20  
    21  	"github.com/juju/juju/api"
    22  	"github.com/juju/juju/apiserver"
    23  	"github.com/juju/juju/juju/testing"
    24  	"github.com/juju/juju/rpc"
    25  	coretesting "github.com/juju/juju/testing"
    26  )
    27  
    28  type pingerSuite struct {
    29  	testing.JujuConnSuite
    30  }
    31  
    32  var _ = gc.Suite(&pingerSuite{})
    33  
    34  var testPingPeriod = 100 * time.Millisecond
    35  
    36  func (s *pingerSuite) TestConnectionBrokenDetection(c *gc.C) {
    37  	s.PatchValue(&api.PingPeriod, testPingPeriod)
    38  
    39  	st, _ := s.OpenAPIAsNewMachine(c)
    40  
    41  	// Connection still alive
    42  	select {
    43  	case <-time.After(testPingPeriod):
    44  	case <-st.Broken():
    45  		c.Fatalf("connection should be alive still")
    46  	}
    47  
    48  	// Close the connection and see if we detect this
    49  	go st.Close()
    50  
    51  	// Check it's detected
    52  	select {
    53  	case <-time.After(testPingPeriod + time.Second):
    54  		c.Fatalf("connection not closed as expected")
    55  	case <-st.Broken():
    56  		return
    57  	}
    58  }
    59  
    60  func (s *pingerSuite) TestPing(c *gc.C) {
    61  	tw := &loggo.TestWriter{}
    62  	c.Assert(loggo.RegisterWriter("ping-tester", tw, loggo.DEBUG), gc.IsNil)
    63  	defer loggo.RemoveWriter("ping-tester")
    64  
    65  	st, _ := s.OpenAPIAsNewMachine(c)
    66  	err := st.Ping()
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	err = st.Close()
    69  	c.Assert(err, jc.ErrorIsNil)
    70  	err = st.Ping()
    71  	c.Assert(errors.Cause(err), gc.Equals, rpc.ErrShutdown)
    72  
    73  	// Make sure that ping messages have not been logged.
    74  	for _, m := range tw.Log() {
    75  		c.Logf("checking %q", m.Message)
    76  		c.Check(m.Message, gc.Not(gc.Matches), `.*"Request":"Ping".*`)
    77  	}
    78  }
    79  
    80  func (s *pingerSuite) TestClientNoNeedToPing(c *gc.C) {
    81  	s.PatchValue(apiserver.MaxClientPingInterval, time.Duration(0))
    82  	st, err := api.Open(s.APIInfo(c), api.DefaultDialOpts())
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	defer st.Close()
    85  	time.Sleep(coretesting.ShortWait)
    86  	err = st.Ping()
    87  	c.Assert(err, jc.ErrorIsNil)
    88  }
    89  
    90  func (s *pingerSuite) TestAgentConnectionShutsDownWithNoPing(c *gc.C) {
    91  	s.PatchValue(apiserver.MaxClientPingInterval, time.Duration(0))
    92  	st, _ := s.OpenAPIAsNewMachine(c)
    93  	time.Sleep(coretesting.ShortWait)
    94  	err := st.Ping()
    95  	c.Assert(err, gc.ErrorMatches, "connection is shut down")
    96  }
    97  
    98  func (s *pingerSuite) calculatePingTimeout(c *gc.C) time.Duration {
    99  	// Try opening an API connection a few times and take the max
   100  	// delay among the attempts.
   101  	attempt := utils.AttemptStrategy{
   102  		Delay: coretesting.ShortWait,
   103  		Min:   3,
   104  	}
   105  	var maxTimeout time.Duration
   106  	for a := attempt.Start(); a.Next(); {
   107  		openStart := time.Now()
   108  		st, _ := s.OpenAPIAsNewMachine(c)
   109  		err := st.Ping()
   110  		if c.Check(err, jc.ErrorIsNil) {
   111  			openDelay := time.Since(openStart)
   112  			c.Logf("API open and initial ping took %v", openDelay)
   113  			if maxTimeout < openDelay {
   114  				maxTimeout = openDelay
   115  			}
   116  		}
   117  		if st != nil {
   118  			c.Check(st.Close(), jc.ErrorIsNil)
   119  		}
   120  	}
   121  	if !c.Failed() && maxTimeout > 0 {
   122  		return maxTimeout
   123  	}
   124  	c.Fatalf("cannot calculate ping timeout")
   125  	return 0
   126  }
   127  
   128  func (s *pingerSuite) TestAgentConnectionDelaysShutdownWithPing(c *gc.C) {
   129  	// To negate the effects of an underpowered or heavily loaded
   130  	// machine running this test, tune the shortTimeout based on the
   131  	// maximum duration it takes to open an API connection.
   132  	shortTimeout := s.calculatePingTimeout(c)
   133  	attemptDelay := shortTimeout / 4
   134  
   135  	s.PatchValue(apiserver.MaxClientPingInterval, time.Duration(shortTimeout))
   136  
   137  	st, _ := s.OpenAPIAsNewMachine(c)
   138  	err := st.Ping()
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	defer st.Close()
   141  
   142  	// As long as we don't wait too long, the connection stays open
   143  	attempt := utils.AttemptStrategy{
   144  		Min:   10,
   145  		Delay: attemptDelay,
   146  	}
   147  	testStart := time.Now()
   148  	c.Logf(
   149  		"pinging %d times with %v delay, ping timeout %v, starting at %v",
   150  		attempt.Min, attempt.Delay, shortTimeout, testStart,
   151  	)
   152  	var lastLoop time.Time
   153  	for a := attempt.Start(); a.Next(); {
   154  		testNow := time.Now()
   155  		loopDelta := testNow.Sub(lastLoop)
   156  		if lastLoop.IsZero() {
   157  			loopDelta = 0
   158  		}
   159  		c.Logf("duration since last ping: %v", loopDelta)
   160  		err = st.Ping()
   161  		if !c.Check(
   162  			err, jc.ErrorIsNil,
   163  			gc.Commentf(
   164  				"ping timeout exceeded at %v (%v since the test start)",
   165  				testNow, testNow.Sub(testStart),
   166  			),
   167  		) {
   168  			c.Check(err, gc.ErrorMatches, "connection is shut down")
   169  			return
   170  		}
   171  		lastLoop = time.Now()
   172  	}
   173  
   174  	// However, once we stop pinging for too long, the connection dies
   175  	time.Sleep(2 * shortTimeout) // Exceed the timeout.
   176  	err = st.Ping()
   177  	c.Assert(err, gc.ErrorMatches, "connection is shut down")
   178  }
   179  
   180  type mongoPingerSuite struct {
   181  	testing.JujuConnSuite
   182  }
   183  
   184  var _ = gc.Suite(&mongoPingerSuite{})
   185  
   186  func (s *mongoPingerSuite) SetUpSuite(c *gc.C) {
   187  	s.JujuConnSuite.SetUpSuite(c)
   188  	// We need to set the ping interval before the server is started in test setup.
   189  	restore := gitjujutesting.PatchValue(apiserver.MongoPingInterval, coretesting.ShortWait)
   190  	s.AddCleanup(func(*gc.C) { restore() })
   191  }
   192  
   193  func (s *mongoPingerSuite) TestAgentConnectionsShutDownWhenStateDies(c *gc.C) {
   194  	st, _ := s.OpenAPIAsNewMachine(c)
   195  	err := st.Ping()
   196  	c.Assert(err, jc.ErrorIsNil)
   197  	gitjujutesting.MgoServer.Destroy()
   198  
   199  	attempt := utils.AttemptStrategy{
   200  		Total: coretesting.LongWait,
   201  		Delay: coretesting.ShortWait,
   202  	}
   203  	for a := attempt.Start(); a.Next(); {
   204  		if err := st.Ping(); err != nil {
   205  			c.Assert(err, gc.ErrorMatches, "connection is shut down")
   206  			return
   207  		}
   208  	}
   209  	c.Fatalf("timed out waiting for API server to die")
   210  }