github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "strings" 11 "sync" 12 "time" 13 14 "github.com/juju/clock" 15 "github.com/juju/clock/testclock" 16 gitjujutesting "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/names.v2" 20 21 "github.com/juju/juju/apiserver/params" 22 "github.com/juju/juju/core/instance" 23 "github.com/juju/juju/core/status" 24 "github.com/juju/juju/network" 25 coretesting "github.com/juju/juju/testing" 26 ) 27 28 var _ = gc.Suite(&machineSuite{}) 29 30 type machineSuite struct { 31 coretesting.BaseSuite 32 } 33 34 var testAddrs = network.NewAddresses("127.0.0.1") 35 36 func (s *machineSuite) TestSetsInstanceInfoInitially(c *gc.C) { 37 context := &testMachineContext{ 38 getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), 39 dyingc: make(chan struct{}), 40 } 41 m := &testMachine{ 42 tag: names.NewMachineTag("99"), 43 instanceId: "i1234", 44 refresh: func() error { return nil }, 45 life: params.Alive, 46 } 47 died := make(chan machine) 48 49 clock := newTestClock() 50 go runMachine(context, m, nil, died, clock) 51 c.Assert(clock.WaitAdvance(LongPoll, coretesting.ShortWait, 1), jc.ErrorIsNil) 52 c.Assert(clock.WaitAdvance(LongPoll, coretesting.ShortWait, 1), jc.ErrorIsNil) 53 54 killMachineLoop(c, m, context.dyingc, died) 55 c.Assert(context.killErr, gc.Equals, nil) 56 c.Assert(m.addresses, gc.DeepEquals, testAddrs) 57 c.Assert(m.setAddressCount, gc.Equals, 1) 58 c.Assert(m.instStatusInfo, gc.Equals, "running") 59 } 60 61 func (s *machineSuite) TestSetsInstanceInfoDeadMachineInitially(c *gc.C) { 62 context := &testMachineContext{ 63 getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "deleting", nil), 64 dyingc: make(chan struct{}), 65 } 66 m := &testMachine{ 67 tag: names.NewMachineTag("99"), 68 instanceId: "i1234", 69 refresh: func() error { return nil }, 70 life: params.Dead, 71 } 72 died := make(chan machine) 73 74 clock := newTestClock() 75 go runMachine(context, m, nil, died, clock) 76 c.Assert(clock.WaitAdvance(LongPoll, coretesting.ShortWait, 1), jc.ErrorIsNil) 77 c.Assert(clock.WaitAdvance(LongPoll, coretesting.ShortWait, 1), jc.ErrorIsNil) 78 79 killMachineLoop(c, m, context.dyingc, died) 80 c.Assert(context.killErr, gc.Equals, nil) 81 c.Assert(m.setAddressCount, gc.Equals, 0) 82 c.Assert(m.instStatusInfo, gc.Equals, "deleting") 83 } 84 85 func (s *machineSuite) TestShortPollIntervalWhenNoAddress(c *gc.C) { 86 s.testShortPoll(c, nil, "i1234", "running", status.Started) 87 } 88 89 func (s *machineSuite) TestShortPollIntervalWhenNoStatus(c *gc.C) { 90 s.testShortPoll(c, testAddrs, "i1234", "", status.Status("")) 91 } 92 93 func (s *machineSuite) TestShortPollIntervalWhenNotStarted(c *gc.C) { 94 s.testShortPoll(c, testAddrs, "i1234", "pending", status.Pending) 95 } 96 97 func (s *machineSuite) testShortPoll( 98 c *gc.C, addrs []network.Address, 99 instId, instStatus string, 100 machineStatus status.Status, 101 ) { 102 clock := newTestClock() 103 testRunMachine(c, addrs, instId, instStatus, machineStatus, clock, func() { 104 c.Assert(clock.WaitAdvance( 105 time.Duration(float64(ShortPoll)*ShortPollBackoff), coretesting.ShortWait, 1), 106 jc.ErrorIsNil, 107 ) 108 }) 109 clock.CheckCall(c, 0, "After", time.Duration(float64(ShortPoll)*ShortPollBackoff)) 110 clock.CheckCall(c, 1, "After", time.Duration(float64(ShortPoll)*ShortPollBackoff*ShortPollBackoff)) 111 } 112 113 func (s *machineSuite) TestNoPollWhenNotProvisioned(c *gc.C) { 114 polled := make(chan struct{}, 1) 115 getInstanceInfo := func(id instance.Id) (instanceInfo, error) { 116 select { 117 case polled <- struct{}{}: 118 default: 119 } 120 return instanceInfo{testAddrs, instance.Status{Status: status.Unknown, Message: "pending"}}, nil 121 } 122 context := &testMachineContext{ 123 getInstanceInfo: getInstanceInfo, 124 dyingc: make(chan struct{}), 125 } 126 m := &testMachine{ 127 tag: names.NewMachineTag("99"), 128 instanceId: instance.Id(""), 129 refresh: func() error { return nil }, 130 addresses: testAddrs, 131 life: params.Alive, 132 status: "pending", 133 } 134 died := make(chan machine) 135 136 clock := testclock.NewClock(time.Time{}) 137 changed := make(chan struct{}) 138 go runMachine(context, m, changed, died, clock) 139 140 expectPoll := func() { 141 c.Assert(clock.WaitAdvance(ShortPoll, coretesting.ShortWait, 1), jc.ErrorIsNil) 142 } 143 144 expectPoll() 145 expectPoll() 146 select { 147 case <-polled: 148 c.Fatalf("unexpected instance poll") 149 case <-time.After(coretesting.ShortWait): 150 } 151 152 m.setInstanceId("inst-ance") 153 expectPoll() 154 select { 155 case <-polled: 156 case <-time.After(coretesting.LongWait): 157 c.Fatalf("expected instance poll") 158 } 159 160 killMachineLoop(c, m, context.dyingc, died) 161 c.Assert(context.killErr, gc.Equals, nil) 162 } 163 164 func (s *machineSuite) TestShortPollBackoffLimit(c *gc.C) { 165 pollDurations := []time.Duration{ 166 2 * time.Second, // ShortPoll 167 4 * time.Second, 168 8 * time.Second, 169 16 * time.Second, 170 32 * time.Second, 171 64 * time.Second, 172 128 * time.Second, 173 256 * time.Second, 174 512 * time.Second, 175 900 * time.Second, // limit is 15 minutes (LongPoll) 176 } 177 178 clock := newTestClock() 179 testRunMachine(c, nil, "i1234", "", status.Started, clock, func() { 180 for _, d := range pollDurations { 181 c.Assert(clock.WaitAdvance(time.Duration(d), coretesting.ShortWait, 1), jc.ErrorIsNil) 182 } 183 }) 184 for i, d := range pollDurations { 185 clock.CheckCall(c, i, "After", d) 186 } 187 } 188 189 func (s *machineSuite) TestLongPollIntervalWhenHasAllInstanceInfo(c *gc.C) { 190 clock := newTestClock() 191 testRunMachine(c, testAddrs, "i1234", "running", status.Started, clock, func() { 192 c.Assert(clock.WaitAdvance(LongPoll, coretesting.ShortWait, 1), jc.ErrorIsNil) 193 }) 194 clock.CheckCall(c, 0, "After", LongPoll) 195 } 196 197 func testRunMachine( 198 c *gc.C, 199 addrs []network.Address, 200 instId, instStatus string, 201 machineStatus status.Status, 202 clock clock.Clock, 203 test func(), 204 ) { 205 getInstanceInfo := func(id instance.Id) (instanceInfo, error) { 206 c.Check(string(id), gc.Equals, instId) 207 if addrs == nil { 208 return instanceInfo{}, fmt.Errorf("no instance addresses available") 209 } 210 return instanceInfo{addrs, instance.Status{Status: status.Unknown, Message: instStatus}}, nil 211 } 212 context := &testMachineContext{ 213 getInstanceInfo: getInstanceInfo, 214 dyingc: make(chan struct{}), 215 } 216 m := &testMachine{ 217 tag: names.NewMachineTag("99"), 218 instanceId: instance.Id(instId), 219 refresh: func() error { return nil }, 220 addresses: addrs, 221 life: params.Alive, 222 status: machineStatus, 223 } 224 died := make(chan machine) 225 226 go runMachine(context, m, nil, died, clock) 227 test() 228 229 killMachineLoop(c, m, context.dyingc, died) 230 c.Assert(context.killErr, gc.Equals, nil) 231 } 232 233 func (*machineSuite) TestChangedRefreshes(c *gc.C) { 234 context := &testMachineContext{ 235 getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), 236 dyingc: make(chan struct{}), 237 } 238 refreshc := make(chan struct{}) 239 m := &testMachine{ 240 tag: names.NewMachineTag("99"), 241 instanceId: "i1234", 242 refresh: func() error { 243 refreshc <- struct{}{} 244 return nil 245 }, 246 addresses: testAddrs, 247 life: params.Dead, 248 } 249 died := make(chan machine) 250 changed := make(chan struct{}) 251 clock := newTestClock() 252 go runMachine(context, m, changed, died, clock) 253 254 c.Assert(clock.WaitAdvance(LongPoll, coretesting.ShortWait, 1), jc.ErrorIsNil) 255 select { 256 case <-died: 257 c.Fatalf("machine died prematurely") 258 case <-time.After(coretesting.ShortWait): 259 } 260 261 // Notify the machine that it has changed; it should 262 // refresh, and publish the fact that it no longer has 263 // an address. 264 changed <- struct{}{} 265 266 select { 267 case <-refreshc: 268 case <-time.After(coretesting.LongWait): 269 c.Fatalf("timed out waiting for refresh") 270 } 271 select { 272 case <-died: 273 case <-time.After(coretesting.LongWait): 274 c.Fatalf("expected death after life set to dying") 275 } 276 // The machine addresses should remain the same even 277 // after death. 278 c.Assert(m.addresses, gc.DeepEquals, testAddrs) 279 } 280 281 var terminatingErrorsTests = []struct { 282 about string 283 mutate func(m *testMachine, err error) 284 }{{ 285 about: "set addresses", 286 mutate: func(m *testMachine, err error) { 287 m.setAddressesErr = err 288 }, 289 }, { 290 about: "refresh", 291 mutate: func(m *testMachine, err error) { 292 m.refresh = func() error { 293 return err 294 } 295 }, 296 }, { 297 about: "instance id", 298 mutate: func(m *testMachine, err error) { 299 m.instanceIdErr = err 300 }, 301 }} 302 303 func (*machineSuite) TestTerminatingErrors(c *gc.C) { 304 for i, test := range terminatingErrorsTests { 305 c.Logf("test %d: %s", i, test.about) 306 testTerminatingErrors(c, test.mutate) 307 } 308 } 309 310 // 311 // testTerminatingErrors checks that when a testMachine is 312 // changed with the given mutate function, the machine goroutine 313 // will die having called its context's killAll function with the 314 // given error. The test is cunningly structured so that it in the normal course 315 // of things it will go through all possible places that can return an error. 316 func testTerminatingErrors(c *gc.C, mutate func(m *testMachine, err error)) { 317 context := &testMachineContext{ 318 getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), 319 dyingc: make(chan struct{}), 320 } 321 expectErr := stderrors.New("a very unusual error") 322 m := &testMachine{ 323 tag: names.NewMachineTag("99"), 324 instanceId: "i1234", 325 refresh: func() error { return nil }, 326 life: params.Alive, 327 } 328 mutate(m, expectErr) 329 died := make(chan machine) 330 changed := make(chan struct{}, 1) 331 go runMachine(context, m, changed, died, newTestClock()) 332 changed <- struct{}{} 333 select { 334 case <-died: 335 case <-time.After(coretesting.LongWait): 336 c.Fatalf("timed out waiting for machine to die") 337 } 338 c.Assert(context.killErr, gc.ErrorMatches, ".*"+expectErr.Error()) 339 } 340 341 func killMachineLoop(c *gc.C, m machine, dying chan struct{}, died <-chan machine) { 342 close(dying) 343 select { 344 case diedm := <-died: 345 c.Assert(diedm, gc.Equals, m) 346 case <-time.After(coretesting.LongWait): 347 c.Fatalf("updater did not die after dying channel was closed") 348 } 349 } 350 351 func instanceInfoGetter( 352 c *gc.C, expectId instance.Id, addrs []network.Address, 353 instanceStatus string, err error) func(id instance.Id) (instanceInfo, error) { 354 355 return func(id instance.Id) (instanceInfo, error) { 356 c.Check(id, gc.Equals, expectId) 357 return instanceInfo{addrs, instance.Status{Status: status.Unknown, Message: instanceStatus}}, err 358 } 359 } 360 361 type testMachineContext struct { 362 killErr error 363 getInstanceInfo func(instance.Id) (instanceInfo, error) 364 dyingc chan struct{} 365 } 366 367 func (context *testMachineContext) kill(err error) { 368 if err == nil { 369 panic("kill with nil error") 370 } 371 context.killErr = err 372 } 373 374 func (context *testMachineContext) instanceInfo(id instance.Id) (instanceInfo, error) { 375 return context.getInstanceInfo(id) 376 } 377 378 func (context *testMachineContext) dying() <-chan struct{} { 379 return context.dyingc 380 } 381 382 func (context *testMachineContext) errDying() error { 383 return nil 384 } 385 386 type testMachine struct { 387 instanceId instance.Id 388 instanceIdErr error 389 tag names.MachineTag 390 instStatus status.Status 391 instStatusInfo string 392 status status.Status 393 refresh func() error 394 setAddressesErr error 395 // mu protects the following fields. 396 mu sync.Mutex 397 life params.Life 398 addresses []network.Address 399 setAddressCount int 400 } 401 402 func (m *testMachine) Tag() names.MachineTag { 403 return m.tag 404 } 405 406 func (m *testMachine) Id() string { 407 return m.tag.Id() 408 } 409 410 func (m *testMachine) ProviderAddresses() ([]network.Address, error) { 411 m.mu.Lock() 412 defer m.mu.Unlock() 413 414 return m.addresses, nil 415 } 416 417 func (m *testMachine) InstanceId() (instance.Id, error) { 418 m.mu.Lock() 419 defer m.mu.Unlock() 420 if m.instanceId == "" { 421 err := ¶ms.Error{ 422 Code: params.CodeNotProvisioned, 423 Message: fmt.Sprintf("machine %v not provisioned", m.Id()), 424 } 425 return "", err 426 } 427 return m.instanceId, m.instanceIdErr 428 } 429 430 func (m *testMachine) setInstanceId(id instance.Id) { 431 m.mu.Lock() 432 defer m.mu.Unlock() 433 m.instanceId = id 434 } 435 436 func (m *testMachine) InstanceNames() (instance.Id, string, error) { 437 instId, err := m.InstanceId() 438 return instId, "", err 439 } 440 441 // This is stubbed out for testing. 442 var MachineStatus = func(m *testMachine) (params.StatusResult, error) { 443 return params.StatusResult{Status: m.status.String()}, nil 444 } 445 446 func (m *testMachine) Status() (params.StatusResult, error) { 447 return MachineStatus(m) 448 } 449 450 func (m *testMachine) IsManual() (bool, error) { 451 return strings.HasPrefix(string(m.instanceId), "manual:"), nil 452 } 453 454 func (m *testMachine) InstanceStatus() (params.StatusResult, error) { 455 m.mu.Lock() 456 defer m.mu.Unlock() 457 return params.StatusResult{Status: m.instStatus.String()}, nil 458 } 459 460 func (m *testMachine) SetInstanceStatus(machineStatus status.Status, info string, data map[string]interface{}) error { 461 m.mu.Lock() 462 defer m.mu.Unlock() 463 m.instStatus = machineStatus 464 m.instStatusInfo = info 465 return nil 466 } 467 468 func (m *testMachine) SetProviderAddresses(addrs ...network.Address) error { 469 if m.setAddressesErr != nil { 470 return m.setAddressesErr 471 } 472 m.mu.Lock() 473 defer m.mu.Unlock() 474 m.addresses = append(m.addresses[:0], addrs...) 475 m.setAddressCount++ 476 return nil 477 } 478 479 func (m *testMachine) String() string { 480 return m.tag.Id() 481 } 482 483 func (m *testMachine) Refresh() error { 484 return m.refresh() 485 } 486 487 func (m *testMachine) Life() params.Life { 488 m.mu.Lock() 489 defer m.mu.Unlock() 490 return m.life 491 } 492 493 type testClock struct { 494 gitjujutesting.Stub 495 *testclock.Clock 496 } 497 498 func newTestClock() *testClock { 499 clock := testclock.NewClock(time.Time{}) 500 return &testClock{Clock: clock} 501 } 502 503 func (t *testClock) After(d time.Duration) <-chan time.Time { 504 t.MethodCall(t, "After", d) 505 return t.Clock.After(d) 506 }