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