launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/instancepoller/machine_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // TODO(wallyworld) - move to instancepoller_test
     5  package instancepoller
     6  
     7  import (
     8  	errgo "launchpad.net/errgo/errors"
     9  	"math"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	gc "launchpad.net/gocheck"
    15  
    16  	"launchpad.net/juju-core/errors"
    17  	"launchpad.net/juju-core/instance"
    18  	"launchpad.net/juju-core/state"
    19  	"launchpad.net/juju-core/state/api/params"
    20  	coretesting "launchpad.net/juju-core/testing"
    21  	jc "launchpad.net/juju-core/testing/checkers"
    22  	"launchpad.net/juju-core/testing/testbase"
    23  )
    24  
    25  var _ = gc.Suite(&machineSuite{})
    26  
    27  type machineSuite struct {
    28  	testbase.LoggingSuite
    29  }
    30  
    31  var testAddrs = []instance.Address{instance.NewAddress("127.0.0.1")}
    32  
    33  func (*machineSuite) TestSetsInstanceInfoInitially(c *gc.C) {
    34  	context := &testMachineContext{
    35  		getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil),
    36  		dyingc:          make(chan struct{}),
    37  	}
    38  	m := &testMachine{
    39  		id:         "99",
    40  		instanceId: "i1234",
    41  		refresh:    func() error { return nil },
    42  		life:       state.Alive,
    43  	}
    44  	died := make(chan machine)
    45  	// Change the poll intervals to be short, so that we know
    46  	// that we've polled (probably) at least a few times.
    47  	defer testbase.PatchValue(&ShortPoll, coretesting.ShortWait/10).Restore()
    48  	defer testbase.PatchValue(&LongPoll, coretesting.ShortWait/10).Restore()
    49  
    50  	go runMachine(context, m, nil, died)
    51  	time.Sleep(coretesting.ShortWait)
    52  
    53  	killMachineLoop(c, m, context.dyingc, died)
    54  	c.Assert(context.killAllErr, gc.Equals, nil)
    55  	c.Assert(m.addresses, gc.DeepEquals, testAddrs)
    56  	c.Assert(m.setAddressCount, gc.Equals, 1)
    57  	c.Assert(m.instStatus, gc.Equals, "running")
    58  }
    59  
    60  func (*machineSuite) TestShortPollIntervalWhenNoAddress(c *gc.C) {
    61  	defer testbase.PatchValue(&ShortPoll, 1*time.Millisecond).Restore()
    62  	defer testbase.PatchValue(&LongPoll, coretesting.LongWait).Restore()
    63  	count := countPolls(c, nil, "running", params.StatusStarted)
    64  	c.Assert(count, jc.GreaterThan, 2)
    65  }
    66  
    67  func (*machineSuite) TestShortPollIntervalWhenNoStatus(c *gc.C) {
    68  	defer testbase.PatchValue(&ShortPoll, 1*time.Millisecond).Restore()
    69  	defer testbase.PatchValue(&LongPoll, coretesting.LongWait).Restore()
    70  	count := countPolls(c, testAddrs, "", params.StatusStarted)
    71  	c.Assert(count, jc.GreaterThan, 2)
    72  }
    73  
    74  func (*machineSuite) TestShortPollIntervalWhenNotStarted(c *gc.C) {
    75  	defer testbase.PatchValue(&ShortPoll, 1*time.Millisecond).Restore()
    76  	defer testbase.PatchValue(&LongPoll, coretesting.LongWait).Restore()
    77  	count := countPolls(c, testAddrs, "pending", params.StatusPending)
    78  	c.Assert(count, jc.GreaterThan, 2)
    79  }
    80  
    81  func (*machineSuite) TestShortPollIntervalExponent(c *gc.C) {
    82  	defer testbase.PatchValue(&ShortPoll, 1*time.Microsecond).Restore()
    83  	defer testbase.PatchValue(&LongPoll, coretesting.LongWait).Restore()
    84  	defer testbase.PatchValue(&ShortPollBackoff, 2.0).Restore()
    85  
    86  	// With an exponent of 2, the maximum number of polls that can
    87  	// occur within the given interval ShortWait is log to the base
    88  	// ShortPollBackoff of ShortWait/ShortPoll, given that sleep will
    89  	// sleep for at least the requested interval.
    90  	maxCount := int(math.Log(float64(coretesting.ShortWait)/float64(ShortPoll))/math.Log(ShortPollBackoff) + 1)
    91  	count := countPolls(c, nil, "", params.StatusStarted)
    92  	c.Assert(count, jc.GreaterThan, 2)
    93  	c.Assert(count, jc.LessThan, maxCount)
    94  	c.Logf("actual count: %v; max %v", count, maxCount)
    95  }
    96  
    97  func (*machineSuite) TestLongPollIntervalWhenHasAllInstanceInfo(c *gc.C) {
    98  	defer testbase.PatchValue(&ShortPoll, coretesting.LongWait).Restore()
    99  	defer testbase.PatchValue(&LongPoll, 1*time.Millisecond).Restore()
   100  	count := countPolls(c, testAddrs, "running", params.StatusStarted)
   101  	c.Assert(count, jc.GreaterThan, 2)
   102  }
   103  
   104  // countPolls sets up a machine loop with the given
   105  // addresses and status to be returned from getInstanceInfo,
   106  // waits for coretesting.ShortWait, and returns the
   107  // number of times the instance is polled.
   108  func countPolls(c *gc.C, addrs []instance.Address, instStatus string, machineStatus params.Status) int {
   109  	count := int32(0)
   110  	getInstanceInfo := func(id instance.Id) (instanceInfo, error) {
   111  		c.Check(id, gc.Equals, instance.Id("i1234"))
   112  		atomic.AddInt32(&count, 1)
   113  		if addrs == nil {
   114  			return instanceInfo{}, errgo.Newf("no instance addresses available")
   115  		}
   116  		return instanceInfo{addrs, instStatus}, nil
   117  	}
   118  	context := &testMachineContext{
   119  		getInstanceInfo: getInstanceInfo,
   120  		dyingc:          make(chan struct{}),
   121  	}
   122  	m := &testMachine{
   123  		id:         "99",
   124  		instanceId: "i1234",
   125  		refresh:    func() error { return nil },
   126  		addresses:  addrs,
   127  		life:       state.Alive,
   128  		status:     machineStatus,
   129  	}
   130  	died := make(chan machine)
   131  
   132  	go runMachine(context, m, nil, died)
   133  
   134  	time.Sleep(coretesting.ShortWait)
   135  	killMachineLoop(c, m, context.dyingc, died)
   136  	c.Assert(context.killAllErr, gc.Equals, nil)
   137  	return int(count)
   138  }
   139  
   140  func (*machineSuite) TestSinglePollWhenInstancInfoUnimplemented(c *gc.C) {
   141  	defer testbase.PatchValue(&ShortPoll, 1*time.Millisecond).Restore()
   142  	defer testbase.PatchValue(&LongPoll, 1*time.Millisecond).Restore()
   143  	count := int32(0)
   144  	getInstanceInfo := func(id instance.Id) (instanceInfo, error) {
   145  		c.Check(id, gc.Equals, instance.Id("i1234"))
   146  		atomic.AddInt32(&count, 1)
   147  		return instanceInfo{}, errors.NewNotImplementedError("instance address")
   148  	}
   149  	context := &testMachineContext{
   150  		getInstanceInfo: getInstanceInfo,
   151  		dyingc:          make(chan struct{}),
   152  	}
   153  	m := &testMachine{
   154  		id:         "99",
   155  		instanceId: "i1234",
   156  		refresh:    func() error { return nil },
   157  		life:       state.Alive,
   158  	}
   159  	died := make(chan machine)
   160  
   161  	go runMachine(context, m, nil, died)
   162  
   163  	time.Sleep(coretesting.ShortWait)
   164  	killMachineLoop(c, m, context.dyingc, died)
   165  	c.Assert(context.killAllErr, gc.Equals, nil)
   166  	c.Assert(count, gc.Equals, int32(1))
   167  }
   168  
   169  func (*machineSuite) TestChangedRefreshes(c *gc.C) {
   170  	context := &testMachineContext{
   171  		getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil),
   172  		dyingc:          make(chan struct{}),
   173  	}
   174  	refreshc := make(chan struct{})
   175  	m := &testMachine{
   176  		id:         "99",
   177  		instanceId: "i1234",
   178  		refresh: func() error {
   179  			refreshc <- struct{}{}
   180  			return nil
   181  		},
   182  		addresses: testAddrs,
   183  		life:      state.Dead,
   184  	}
   185  	died := make(chan machine)
   186  	changed := make(chan struct{})
   187  	go runMachine(context, m, changed, died)
   188  	select {
   189  	case <-died:
   190  		c.Fatalf("machine died prematurely")
   191  	case <-time.After(coretesting.ShortWait):
   192  	}
   193  
   194  	// Notify the machine that it has changed; it should
   195  	// refresh, and publish the fact that it no longer has
   196  	// an address.
   197  	changed <- struct{}{}
   198  
   199  	select {
   200  	case <-refreshc:
   201  	case <-time.After(coretesting.LongWait):
   202  		c.Fatalf("timed out waiting for refresh")
   203  	}
   204  	select {
   205  	case <-died:
   206  	case <-time.After(coretesting.LongWait):
   207  		c.Fatalf("expected death after life set to dying")
   208  	}
   209  	// The machine addresses should remain the same even
   210  	// after death.
   211  	c.Assert(m.addresses, gc.DeepEquals, testAddrs)
   212  }
   213  
   214  var terminatingErrorsTests = []struct {
   215  	about  string
   216  	mutate func(m *testMachine, err error)
   217  }{{
   218  	about: "set addresses",
   219  	mutate: func(m *testMachine, err error) {
   220  		m.setAddressesErr = err
   221  	},
   222  }, {
   223  	about: "refresh",
   224  	mutate: func(m *testMachine, err error) {
   225  		m.refresh = func() error {
   226  			return err
   227  		}
   228  	},
   229  }, {
   230  	about: "instance id",
   231  	mutate: func(m *testMachine, err error) {
   232  		m.instanceIdErr = err
   233  	},
   234  }}
   235  
   236  func (*machineSuite) TestTerminatingErrors(c *gc.C) {
   237  	for i, test := range terminatingErrorsTests {
   238  		c.Logf("test %d: %s", i, test.about)
   239  		testTerminatingErrors(c, test.mutate)
   240  	}
   241  }
   242  
   243  //
   244  // testTerminatingErrors checks that when a testMachine is
   245  // changed with the given mutate function, the machine goroutine
   246  // will die having called its context's killAll function with the
   247  // given error.  The test is cunningly structured so that it in the normal course
   248  // of things it will go through all possible places that can return an error.
   249  func testTerminatingErrors(c *gc.C, mutate func(m *testMachine, err error)) {
   250  	context := &testMachineContext{
   251  		getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil),
   252  		dyingc:          make(chan struct{}),
   253  	}
   254  	expectErr := errgo.New("a very unusual error")
   255  	m := &testMachine{
   256  		id:         "99",
   257  		instanceId: "i1234",
   258  		refresh:    func() error { return nil },
   259  		life:       state.Alive,
   260  	}
   261  	mutate(m, expectErr)
   262  	died := make(chan machine)
   263  	changed := make(chan struct{}, 1)
   264  	go runMachine(context, m, changed, died)
   265  	changed <- struct{}{}
   266  	select {
   267  	case <-died:
   268  	case <-time.After(coretesting.LongWait):
   269  		c.Fatalf("timed out waiting for machine to die")
   270  	}
   271  	c.Assert(context.killAllErr, gc.ErrorMatches, ".*"+expectErr.Error())
   272  }
   273  
   274  func killMachineLoop(c *gc.C, m machine, dying chan struct{}, died <-chan machine) {
   275  	close(dying)
   276  	select {
   277  	case diedm := <-died:
   278  		c.Assert(diedm, gc.Equals, m)
   279  	case <-time.After(coretesting.LongWait):
   280  		c.Fatalf("updater did not die after dying channel was closed")
   281  	}
   282  }
   283  
   284  func instanceInfoGetter(
   285  	c *gc.C, expectId instance.Id, addrs []instance.Address,
   286  	status string, err error) func(id instance.Id) (instanceInfo, error) {
   287  
   288  	return func(id instance.Id) (instanceInfo, error) {
   289  		c.Check(id, gc.Equals, expectId)
   290  		return instanceInfo{addrs, status}, err
   291  	}
   292  }
   293  
   294  type testMachineContext struct {
   295  	killAllErr      error
   296  	getInstanceInfo func(instance.Id) (instanceInfo, error)
   297  	dyingc          chan struct{}
   298  }
   299  
   300  func (context *testMachineContext) killAll(err error) {
   301  	if err == nil {
   302  		panic("killAll with nil error")
   303  	}
   304  	context.killAllErr = err
   305  }
   306  
   307  func (context *testMachineContext) instanceInfo(id instance.Id) (instanceInfo, error) {
   308  	return context.getInstanceInfo(id)
   309  }
   310  
   311  func (context *testMachineContext) dying() <-chan struct{} {
   312  	return context.dyingc
   313  }
   314  
   315  type testMachine struct {
   316  	instanceId      instance.Id
   317  	instanceIdErr   error
   318  	id              string
   319  	instStatus      string
   320  	status          params.Status
   321  	refresh         func() error
   322  	setAddressesErr error
   323  	// mu protects the following fields.
   324  	mu              sync.Mutex
   325  	life            state.Life
   326  	addresses       []instance.Address
   327  	setAddressCount int
   328  }
   329  
   330  func (m *testMachine) Id() string {
   331  	if m.id == "" {
   332  		panic("Id called but not set")
   333  	}
   334  	return m.id
   335  }
   336  
   337  func (m *testMachine) Addresses() []instance.Address {
   338  	m.mu.Lock()
   339  	defer m.mu.Unlock()
   340  	return m.addresses
   341  }
   342  
   343  func (m *testMachine) InstanceId() (instance.Id, error) {
   344  	return m.instanceId, m.instanceIdErr
   345  }
   346  
   347  func (m *testMachine) Status() (status params.Status, info string, data params.StatusData, err error) {
   348  	return m.status, "", nil, nil
   349  }
   350  
   351  func (m *testMachine) InstanceStatus() (string, error) {
   352  	m.mu.Lock()
   353  	defer m.mu.Unlock()
   354  	return m.instStatus, nil
   355  }
   356  
   357  func (m *testMachine) SetInstanceStatus(status string) error {
   358  	m.mu.Lock()
   359  	defer m.mu.Unlock()
   360  	m.instStatus = status
   361  	return nil
   362  }
   363  
   364  func (m *testMachine) SetAddresses(addrs []instance.Address) error {
   365  	if m.setAddressesErr != nil {
   366  		return m.setAddressesErr
   367  	}
   368  	m.mu.Lock()
   369  	defer m.mu.Unlock()
   370  	m.addresses = append(m.addresses[:0], addrs...)
   371  	m.setAddressCount++
   372  	return nil
   373  }
   374  
   375  func (m *testMachine) String() string {
   376  	return m.id
   377  }
   378  
   379  func (m *testMachine) Refresh() error {
   380  	return m.refresh()
   381  }
   382  
   383  func (m *testMachine) Life() state.Life {
   384  	m.mu.Lock()
   385  	defer m.mu.Unlock()
   386  	return m.life
   387  }
   388  
   389  func (m *testMachine) setLife(life state.Life) {
   390  	m.mu.Lock()
   391  	defer m.mu.Unlock()
   392  	m.life = life
   393  }