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