github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/worker/instancepoller/aggregate_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package instancepoller
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "launchpad.net/gocheck"
    15  
    16  	"github.com/juju/juju/environs"
    17  	"github.com/juju/juju/instance"
    18  	"github.com/juju/juju/network"
    19  	"github.com/juju/juju/testing"
    20  )
    21  
    22  type aggregateSuite struct {
    23  	testing.BaseSuite
    24  }
    25  
    26  var _ = gc.Suite(&aggregateSuite{})
    27  
    28  type testInstance struct {
    29  	instance.Instance
    30  	addresses []network.Address
    31  	status    string
    32  	err       error
    33  }
    34  
    35  var _ instance.Instance = (*testInstance)(nil)
    36  
    37  func (t *testInstance) Addresses() ([]network.Address, error) {
    38  	if t.err != nil {
    39  		return nil, t.err
    40  	}
    41  	return t.addresses, nil
    42  }
    43  
    44  func (t *testInstance) Status() string {
    45  	return t.status
    46  }
    47  
    48  type testInstanceGetter struct {
    49  	// ids is set when the Instances method is called.
    50  	ids     []instance.Id
    51  	results []instance.Instance
    52  	err     error
    53  	counter int32
    54  }
    55  
    56  func (i *testInstanceGetter) Instances(ids []instance.Id) (result []instance.Instance, err error) {
    57  	i.ids = ids
    58  	atomic.AddInt32(&i.counter, 1)
    59  	return i.results, i.err
    60  }
    61  
    62  func newTestInstance(status string, addresses []string) *testInstance {
    63  	thisInstance := testInstance{status: status}
    64  	thisInstance.addresses = network.NewAddresses(addresses...)
    65  	return &thisInstance
    66  }
    67  
    68  func (s *aggregateSuite) TestSingleRequest(c *gc.C) {
    69  	testGetter := new(testInstanceGetter)
    70  	instance1 := newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"})
    71  	testGetter.results = []instance.Instance{instance1}
    72  	aggregator := newAggregator(testGetter)
    73  
    74  	info, err := aggregator.instanceInfo("foo")
    75  	c.Assert(err, gc.IsNil)
    76  	c.Assert(info, gc.DeepEquals, instanceInfo{
    77  		status:    "foobar",
    78  		addresses: instance1.addresses,
    79  	})
    80  	c.Assert(testGetter.ids, gc.DeepEquals, []instance.Id{"foo"})
    81  }
    82  
    83  func (s *aggregateSuite) TestMultipleResponseHandling(c *gc.C) {
    84  	s.PatchValue(&gatherTime, 30*time.Millisecond)
    85  	testGetter := new(testInstanceGetter)
    86  
    87  	instance1 := newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"})
    88  	testGetter.results = []instance.Instance{instance1}
    89  	aggregator := newAggregator(testGetter)
    90  
    91  	replyChan := make(chan instanceInfoReply)
    92  	req := instanceInfoReq{
    93  		reply:  replyChan,
    94  		instId: instance.Id("foo"),
    95  	}
    96  	aggregator.reqc <- req
    97  	reply := <-replyChan
    98  	c.Assert(reply.err, gc.IsNil)
    99  
   100  	instance2 := newTestInstance("not foobar", []string{"192.168.1.2"})
   101  	instance3 := newTestInstance("ok-ish", []string{"192.168.1.3"})
   102  	testGetter.results = []instance.Instance{instance2, instance3}
   103  
   104  	var wg sync.WaitGroup
   105  	checkInfo := func(id instance.Id, expectStatus string) {
   106  		info, err := aggregator.instanceInfo(id)
   107  		c.Check(err, gc.IsNil)
   108  		c.Check(info.status, gc.Equals, expectStatus)
   109  		wg.Done()
   110  	}
   111  
   112  	wg.Add(2)
   113  	go checkInfo("foo2", "not foobar")
   114  	go checkInfo("foo3", "ok-ish")
   115  	wg.Wait()
   116  
   117  	c.Assert(len(testGetter.ids), gc.DeepEquals, 2)
   118  }
   119  
   120  type batchingInstanceGetter struct {
   121  	testInstanceGetter
   122  	wg         sync.WaitGroup
   123  	aggregator *aggregator
   124  	batchSize  int
   125  	started    int
   126  }
   127  
   128  func (g *batchingInstanceGetter) Instances(ids []instance.Id) ([]instance.Instance, error) {
   129  	insts, err := g.testInstanceGetter.Instances(ids)
   130  	g.startRequests()
   131  	return insts, err
   132  }
   133  
   134  func (g *batchingInstanceGetter) startRequests() {
   135  	n := len(g.results) - g.started
   136  	if n > g.batchSize {
   137  		n = g.batchSize
   138  	}
   139  	for i := 0; i < n; i++ {
   140  		g.startRequest()
   141  	}
   142  }
   143  
   144  func (g *batchingInstanceGetter) startRequest() {
   145  	g.started++
   146  	go func() {
   147  		_, err := g.aggregator.instanceInfo("foo")
   148  		if err != nil {
   149  			panic(err)
   150  		}
   151  		g.wg.Done()
   152  	}()
   153  }
   154  
   155  func (s *aggregateSuite) TestBatching(c *gc.C) {
   156  	s.PatchValue(&gatherTime, 10*time.Millisecond)
   157  	var testGetter batchingInstanceGetter
   158  	testGetter.aggregator = newAggregator(&testGetter)
   159  	testGetter.results = make([]instance.Instance, 100)
   160  	for i := range testGetter.results {
   161  		testGetter.results[i] = newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"})
   162  	}
   163  	testGetter.batchSize = 10
   164  	testGetter.wg.Add(len(testGetter.results))
   165  	testGetter.startRequest()
   166  	testGetter.wg.Wait()
   167  	c.Assert(testGetter.counter, gc.Equals, int32(len(testGetter.results)/testGetter.batchSize)+1)
   168  }
   169  
   170  func (s *aggregateSuite) TestError(c *gc.C) {
   171  	testGetter := new(testInstanceGetter)
   172  	ourError := fmt.Errorf("Some error")
   173  	testGetter.err = ourError
   174  
   175  	aggregator := newAggregator(testGetter)
   176  
   177  	_, err := aggregator.instanceInfo("foo")
   178  	c.Assert(err, gc.Equals, ourError)
   179  }
   180  
   181  func (s *aggregateSuite) TestPartialErrResponse(c *gc.C) {
   182  	testGetter := new(testInstanceGetter)
   183  	testGetter.err = environs.ErrPartialInstances
   184  	testGetter.results = []instance.Instance{nil}
   185  
   186  	aggregator := newAggregator(testGetter)
   187  	_, err := aggregator.instanceInfo("foo")
   188  
   189  	c.Assert(err, gc.ErrorMatches, "instance foo not found")
   190  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   191  }
   192  
   193  func (s *aggregateSuite) TestAddressesError(c *gc.C) {
   194  	testGetter := new(testInstanceGetter)
   195  	instance1 := newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"})
   196  	ourError := fmt.Errorf("gotcha")
   197  	instance1.err = ourError
   198  	testGetter.results = []instance.Instance{instance1}
   199  
   200  	aggregator := newAggregator(testGetter)
   201  	_, err := aggregator.instanceInfo("foo")
   202  	c.Assert(err, gc.Equals, ourError)
   203  }
   204  
   205  func (s *aggregateSuite) TestKillAndWait(c *gc.C) {
   206  	testGetter := new(testInstanceGetter)
   207  	aggregator := newAggregator(testGetter)
   208  	aggregator.Kill()
   209  	err := aggregator.Wait()
   210  	c.Assert(err, gc.IsNil)
   211  }