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 }