github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "gopkg.in/check.v1" 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 id instance.Id 31 addresses []network.Address 32 status string 33 err error 34 } 35 36 var _ instance.Instance = (*testInstance)(nil) 37 38 func (t *testInstance) Id() instance.Id { 39 return t.id 40 } 41 42 func (t *testInstance) Addresses() ([]network.Address, error) { 43 if t.err != nil { 44 return nil, t.err 45 } 46 return t.addresses, nil 47 } 48 49 func (t *testInstance) Status() string { 50 return t.status 51 } 52 53 type testInstanceGetter struct { 54 // ids is set when the Instances method is called. 55 ids []instance.Id 56 results map[instance.Id]instance.Instance 57 err error 58 counter int32 59 } 60 61 func (tig *testInstanceGetter) Instances(ids []instance.Id) (result []instance.Instance, err error) { 62 tig.ids = ids 63 atomic.AddInt32(&tig.counter, 1) 64 results := make([]instance.Instance, len(ids)) 65 for i, id := range ids { 66 // We don't check 'ok' here, because we want the Instance{nil} 67 // response for those 68 results[i] = tig.results[id] 69 } 70 return results, tig.err 71 } 72 73 func (tig *testInstanceGetter) newTestInstance(id instance.Id, status string, addresses []string) *testInstance { 74 if tig.results == nil { 75 tig.results = make(map[instance.Id]instance.Instance) 76 } 77 thisInstance := &testInstance{ 78 id: id, 79 status: status, 80 addresses: network.NewAddresses(addresses...), 81 } 82 tig.results[thisInstance.Id()] = thisInstance 83 return thisInstance 84 } 85 86 func (s *aggregateSuite) TestSingleRequest(c *gc.C) { 87 testGetter := new(testInstanceGetter) 88 instance1 := testGetter.newTestInstance("foo", "foobar", []string{"127.0.0.1", "192.168.1.1"}) 89 aggregator := newAggregator(testGetter) 90 91 info, err := aggregator.instanceInfo("foo") 92 c.Assert(err, jc.ErrorIsNil) 93 c.Assert(info, gc.DeepEquals, instanceInfo{ 94 status: "foobar", 95 addresses: instance1.addresses, 96 }) 97 c.Assert(testGetter.ids, gc.DeepEquals, []instance.Id{"foo"}) 98 } 99 100 func (s *aggregateSuite) TestMultipleResponseHandling(c *gc.C) { 101 s.PatchValue(&gatherTime, 30*time.Millisecond) 102 testGetter := new(testInstanceGetter) 103 104 testGetter.newTestInstance("foo", "foobar", []string{"127.0.0.1", "192.168.1.1"}) 105 aggregator := newAggregator(testGetter) 106 107 replyChan := make(chan instanceInfoReply) 108 req := instanceInfoReq{ 109 reply: replyChan, 110 instId: instance.Id("foo"), 111 } 112 aggregator.reqc <- req 113 reply := <-replyChan 114 c.Assert(reply.err, gc.IsNil) 115 116 testGetter.newTestInstance("foo2", "not foobar", []string{"192.168.1.2"}) 117 testGetter.newTestInstance("foo3", "ok-ish", []string{"192.168.1.3"}) 118 119 var wg sync.WaitGroup 120 checkInfo := func(id instance.Id, expectStatus string) { 121 info, err := aggregator.instanceInfo(id) 122 c.Check(err, jc.ErrorIsNil) 123 c.Check(info.status, gc.Equals, expectStatus) 124 wg.Done() 125 } 126 127 wg.Add(2) 128 go checkInfo("foo2", "not foobar") 129 go checkInfo("foo3", "ok-ish") 130 wg.Wait() 131 132 c.Assert(len(testGetter.ids), gc.DeepEquals, 2) 133 } 134 135 type batchingInstanceGetter struct { 136 testInstanceGetter 137 wg sync.WaitGroup 138 aggregator *aggregator 139 totalCount int 140 batchSize int 141 started int 142 } 143 144 func (g *batchingInstanceGetter) Instances(ids []instance.Id) ([]instance.Instance, error) { 145 insts, err := g.testInstanceGetter.Instances(ids) 146 g.startRequests() 147 return insts, err 148 } 149 150 func (g *batchingInstanceGetter) startRequests() { 151 n := g.totalCount - g.started 152 if n > g.batchSize { 153 n = g.batchSize 154 } 155 for i := 0; i < n; i++ { 156 g.startRequest() 157 } 158 } 159 160 func (g *batchingInstanceGetter) startRequest() { 161 g.started++ 162 go func() { 163 _, err := g.aggregator.instanceInfo("foo") 164 if err != nil { 165 panic(err) 166 } 167 g.wg.Done() 168 }() 169 } 170 171 func (s *aggregateSuite) TestBatching(c *gc.C) { 172 s.PatchValue(&gatherTime, 10*time.Millisecond) 173 var testGetter batchingInstanceGetter 174 testGetter.aggregator = newAggregator(&testGetter) 175 // We only need to inform the system about 1 instance, because all the 176 // requests are for the same instance. 177 testGetter.newTestInstance("foo", "foobar", []string{"127.0.0.1", "192.168.1.1"}) 178 testGetter.totalCount = 100 179 testGetter.batchSize = 10 180 testGetter.wg.Add(testGetter.totalCount) 181 // startRequest will trigger one request, which ends up calling 182 // Instances, which will turn around and trigger batchSize requests, 183 // which should get aggregated into a single call to Instances, which 184 // then should trigger another round of batchSize requests. 185 testGetter.startRequest() 186 testGetter.wg.Wait() 187 c.Assert(testGetter.counter, gc.Equals, int32(testGetter.totalCount/testGetter.batchSize)+1) 188 } 189 190 func (s *aggregateSuite) TestError(c *gc.C) { 191 testGetter := new(testInstanceGetter) 192 ourError := fmt.Errorf("Some error") 193 testGetter.err = ourError 194 195 aggregator := newAggregator(testGetter) 196 197 _, err := aggregator.instanceInfo("foo") 198 c.Assert(err, gc.Equals, ourError) 199 } 200 201 func (s *aggregateSuite) TestPartialErrResponse(c *gc.C) { 202 testGetter := new(testInstanceGetter) 203 testGetter.err = environs.ErrPartialInstances 204 205 aggregator := newAggregator(testGetter) 206 _, err := aggregator.instanceInfo("foo") 207 208 c.Assert(err, gc.ErrorMatches, "instance foo not found") 209 c.Assert(err, jc.Satisfies, errors.IsNotFound) 210 } 211 212 func (s *aggregateSuite) TestAddressesError(c *gc.C) { 213 testGetter := new(testInstanceGetter) 214 instance1 := testGetter.newTestInstance("foo", "foobar", []string{"127.0.0.1", "192.168.1.1"}) 215 ourError := fmt.Errorf("gotcha") 216 instance1.err = ourError 217 218 aggregator := newAggregator(testGetter) 219 _, err := aggregator.instanceInfo("foo") 220 c.Assert(err, gc.Equals, ourError) 221 } 222 223 func (s *aggregateSuite) TestKillAndWait(c *gc.C) { 224 testGetter := new(testInstanceGetter) 225 aggregator := newAggregator(testGetter) 226 aggregator.Kill() 227 err := aggregator.Wait() 228 c.Assert(err, jc.ErrorIsNil) 229 }