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