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 := ¶ms.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 := ¶ms.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 }