github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/state/leadership/manager_block_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package leadership_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/testing"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/state/leadership"
    14  	"github.com/juju/juju/state/lease"
    15  )
    16  
    17  type BlockUntilLeadershipReleasedSuite struct {
    18  	testing.IsolationSuite
    19  }
    20  
    21  var _ = gc.Suite(&BlockUntilLeadershipReleasedSuite{})
    22  
    23  func (s *BlockUntilLeadershipReleasedSuite) TestLeadershipNotHeld(c *gc.C) {
    24  	fix := &Fixture{}
    25  	fix.RunTest(c, func(manager leadership.ManagerWorker, _ *Clock) {
    26  		blockTest := newBlockTest(manager, "redis")
    27  		err := blockTest.assertUnblocked(c)
    28  		c.Check(err, jc.ErrorIsNil)
    29  	})
    30  }
    31  
    32  func (s *BlockUntilLeadershipReleasedSuite) TestLeadershipExpires(c *gc.C) {
    33  	fix := &Fixture{
    34  		leases: map[string]lease.Info{
    35  			"redis": lease.Info{
    36  				Holder: "redis/0",
    37  				Expiry: offset(time.Second),
    38  			},
    39  		},
    40  		expectCalls: []call{{
    41  			method: "ExpireLease",
    42  			args:   []interface{}{"redis"},
    43  			callback: func(leases map[string]lease.Info) {
    44  				delete(leases, "redis")
    45  			},
    46  		}},
    47  	}
    48  	fix.RunTest(c, func(manager leadership.ManagerWorker, clock *Clock) {
    49  		blockTest := newBlockTest(manager, "redis")
    50  		blockTest.assertBlocked(c)
    51  
    52  		// Trigger expiry.
    53  		clock.Advance(time.Second)
    54  		err := blockTest.assertUnblocked(c)
    55  		c.Check(err, jc.ErrorIsNil)
    56  	})
    57  }
    58  
    59  func (s *BlockUntilLeadershipReleasedSuite) TestLeadershipChanged(c *gc.C) {
    60  	fix := &Fixture{
    61  		leases: map[string]lease.Info{
    62  			"redis": lease.Info{
    63  				Holder: "redis/0",
    64  				Expiry: offset(time.Second),
    65  			},
    66  		},
    67  		expectCalls: []call{{
    68  			method: "ExpireLease",
    69  			args:   []interface{}{"redis"},
    70  			err:    lease.ErrInvalid,
    71  			callback: func(leases map[string]lease.Info) {
    72  				leases["redis"] = lease.Info{
    73  					Holder: "redis/99",
    74  					Expiry: offset(time.Minute),
    75  				}
    76  			},
    77  		}},
    78  	}
    79  	fix.RunTest(c, func(manager leadership.ManagerWorker, clock *Clock) {
    80  		blockTest := newBlockTest(manager, "redis")
    81  		blockTest.assertBlocked(c)
    82  
    83  		// Trigger abortive expiry.
    84  		clock.Advance(time.Second)
    85  		blockTest.assertBlocked(c)
    86  	})
    87  }
    88  
    89  func (s *BlockUntilLeadershipReleasedSuite) TestLeadershipExpiredEarly(c *gc.C) {
    90  	fix := &Fixture{
    91  		leases: map[string]lease.Info{
    92  			"redis": lease.Info{
    93  				Holder: "redis/0",
    94  				Expiry: offset(time.Second),
    95  			},
    96  		},
    97  		expectCalls: []call{{
    98  			method: "Refresh",
    99  			callback: func(leases map[string]lease.Info) {
   100  				delete(leases, "redis")
   101  			},
   102  		}},
   103  	}
   104  	fix.RunTest(c, func(manager leadership.ManagerWorker, clock *Clock) {
   105  		blockTest := newBlockTest(manager, "redis")
   106  		blockTest.assertBlocked(c)
   107  
   108  		// Induce a refresh by making an unexpected check; it turns out the
   109  		// lease had already been expired by someone else.
   110  		manager.CheckLeadership("redis", "redis/99")
   111  		err := blockTest.assertUnblocked(c)
   112  		c.Check(err, jc.ErrorIsNil)
   113  	})
   114  }
   115  
   116  func (s *BlockUntilLeadershipReleasedSuite) TestMultiple(c *gc.C) {
   117  	fix := &Fixture{
   118  		leases: map[string]lease.Info{
   119  			"redis": lease.Info{
   120  				Holder: "redis/0",
   121  				Expiry: offset(time.Second),
   122  			},
   123  			"store": lease.Info{
   124  				Holder: "store/0",
   125  				Expiry: offset(time.Second),
   126  			},
   127  		},
   128  		expectCalls: []call{{
   129  			method: "ExpireLease",
   130  			args:   []interface{}{"redis"},
   131  			err:    lease.ErrInvalid,
   132  			callback: func(leases map[string]lease.Info) {
   133  				delete(leases, "redis")
   134  				leases["store"] = lease.Info{
   135  					Holder: "store/9",
   136  					Expiry: offset(time.Minute),
   137  				}
   138  			},
   139  		}, {
   140  			method: "ExpireLease",
   141  			args:   []interface{}{"store"},
   142  			err:    lease.ErrInvalid,
   143  		}},
   144  	}
   145  	fix.RunTest(c, func(manager leadership.ManagerWorker, clock *Clock) {
   146  		redisTest1 := newBlockTest(manager, "redis")
   147  		redisTest1.assertBlocked(c)
   148  		redisTest2 := newBlockTest(manager, "redis")
   149  		redisTest2.assertBlocked(c)
   150  		storeTest1 := newBlockTest(manager, "store")
   151  		storeTest1.assertBlocked(c)
   152  		storeTest2 := newBlockTest(manager, "store")
   153  		storeTest2.assertBlocked(c)
   154  
   155  		// Induce attempted expiry; redis was expired already, store was
   156  		// refreshed and not expired.
   157  		clock.Advance(time.Second)
   158  		err := redisTest2.assertUnblocked(c)
   159  		c.Check(err, jc.ErrorIsNil)
   160  		err = redisTest1.assertUnblocked(c)
   161  		c.Check(err, jc.ErrorIsNil)
   162  		storeTest2.assertBlocked(c)
   163  		storeTest1.assertBlocked(c)
   164  	})
   165  }
   166  
   167  func (s *BlockUntilLeadershipReleasedSuite) TestKillManager(c *gc.C) {
   168  	fix := &Fixture{
   169  		leases: map[string]lease.Info{
   170  			"redis": lease.Info{
   171  				Holder: "redis/0",
   172  				Expiry: offset(time.Second),
   173  			},
   174  		},
   175  	}
   176  	fix.RunTest(c, func(manager leadership.ManagerWorker, _ *Clock) {
   177  		blockTest := newBlockTest(manager, "redis")
   178  		blockTest.assertBlocked(c)
   179  
   180  		manager.Kill()
   181  		err := blockTest.assertUnblocked(c)
   182  		c.Check(err, gc.ErrorMatches, "leadership manager stopped")
   183  	})
   184  }
   185  
   186  // blockTest wraps a goroutine running BlockUntilLeadershipReleased, and
   187  // fails if it's used more than a second after creation (which should be
   188  // *plenty* of time).
   189  type blockTest struct {
   190  	manager     leadership.Manager
   191  	serviceName string
   192  	done        chan error
   193  	abort       <-chan time.Time
   194  }
   195  
   196  // newBlockTest starts a test goroutine blocking until the manager confirms
   197  // leaderlessness of the named service.
   198  func newBlockTest(manager leadership.Manager, serviceName string) *blockTest {
   199  	bt := &blockTest{
   200  		manager:     manager,
   201  		serviceName: serviceName,
   202  		done:        make(chan error),
   203  		abort:       time.After(time.Second),
   204  	}
   205  	go func() {
   206  		select {
   207  		case <-bt.abort:
   208  		case bt.done <- bt.manager.BlockUntilLeadershipReleased(bt.serviceName):
   209  		}
   210  	}()
   211  	return bt
   212  }
   213  
   214  func (bt *blockTest) assertBlocked(c *gc.C) {
   215  	select {
   216  	case err := <-bt.done:
   217  		c.Fatalf("unblocked unexpectedly with %v", err)
   218  	default:
   219  	}
   220  }
   221  
   222  func (bt *blockTest) assertUnblocked(c *gc.C) error {
   223  	select {
   224  	case err := <-bt.done:
   225  		return err
   226  	case <-bt.abort:
   227  		c.Fatalf("timed out before unblocking")
   228  	}
   229  	panic("unreachable")
   230  }