github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "github.com/juju/juju/status" 24 coretesting "github.com/juju/juju/testing" 25 ) 26 27 var _ = gc.Suite(&machineSuite{}) 28 29 type machineSuite struct { 30 coretesting.BaseSuite 31 } 32 33 var testAddrs = network.NewAddresses("127.0.0.1") 34 35 func (s *machineSuite) TestSetsInstanceInfoInitially(c *gc.C) { 36 context := &testMachineContext{ 37 getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), 38 dyingc: make(chan struct{}), 39 } 40 m := &testMachine{ 41 tag: names.NewMachineTag("99"), 42 instanceId: "i1234", 43 refresh: func() error { return nil }, 44 life: params.Alive, 45 } 46 died := make(chan machine) 47 // Change the poll intervals to be short, so that we know 48 // that we've polled (probably) at least a few times. 49 s.PatchValue(&ShortPoll, coretesting.ShortWait/10) 50 s.PatchValue(&LongPoll, coretesting.ShortWait/10) 51 52 go runMachine(context, m, nil, died) 53 time.Sleep(coretesting.ShortWait) 54 55 killMachineLoop(c, m, context.dyingc, died) 56 c.Assert(context.killErr, gc.Equals, nil) 57 c.Assert(m.addresses, gc.DeepEquals, testAddrs) 58 c.Assert(m.setAddressCount, gc.Equals, 1) 59 c.Assert(m.instStatusInfo, gc.Equals, "running") 60 } 61 62 func (s *machineSuite) TestShortPollIntervalWhenNoAddress(c *gc.C) { 63 s.PatchValue(&ShortPoll, 1*time.Millisecond) 64 s.PatchValue(&LongPoll, coretesting.LongWait) 65 count := countPolls(c, nil, "i1234", "running", status.StatusStarted) 66 c.Assert(count, jc.GreaterThan, 2) 67 } 68 69 func (s *machineSuite) TestShortPollIntervalWhenNoStatus(c *gc.C) { 70 s.PatchValue(&ShortPoll, 1*time.Millisecond) 71 s.PatchValue(&LongPoll, coretesting.LongWait) 72 count := countPolls(c, testAddrs, "i1234", "", status.Status("")) 73 c.Assert(count, jc.GreaterThan, 2) 74 } 75 76 func (s *machineSuite) TestShortPollIntervalWhenNotStarted(c *gc.C) { 77 s.PatchValue(&ShortPoll, 1*time.Millisecond) 78 s.PatchValue(&LongPoll, coretesting.LongWait) 79 count := countPolls(c, testAddrs, "i1234", "pending", status.StatusPending) 80 c.Assert(count, jc.GreaterThan, 2) 81 } 82 83 func (s *machineSuite) TestShortPollIntervalWhenNotProvisioned(c *gc.C) { 84 s.PatchValue(&ShortPoll, 1*time.Millisecond) 85 s.PatchValue(&LongPoll, coretesting.LongWait) 86 count := countPolls(c, testAddrs, "", "pending", status.StatusPending) 87 c.Assert(count, gc.Equals, 0) 88 } 89 90 func (s *machineSuite) TestShortPollIntervalExponent(c *gc.C) { 91 s.PatchValue(&ShortPoll, 1*time.Microsecond) 92 s.PatchValue(&LongPoll, coretesting.LongWait) 93 s.PatchValue(&ShortPollBackoff, 2.0) 94 95 // With an exponent of 2, the maximum number of polls that can 96 // occur within the given interval ShortWait is log to the base 97 // ShortPollBackoff of ShortWait/ShortPoll, given that sleep will 98 // sleep for at least the requested interval. 99 maxCount := int(math.Log(float64(coretesting.ShortWait)/float64(ShortPoll))/math.Log(ShortPollBackoff) + 1) 100 count := countPolls(c, nil, "i1234", "", status.StatusStarted) 101 c.Assert(count, jc.GreaterThan, 2) 102 c.Assert(count, jc.LessThan, maxCount) 103 c.Logf("actual count: %v; max %v", count, maxCount) 104 } 105 106 func (s *machineSuite) TestLongPollIntervalWhenHasAllInstanceInfo(c *gc.C) { 107 s.PatchValue(&ShortPoll, coretesting.LongWait) 108 s.PatchValue(&LongPoll, 1*time.Millisecond) 109 count := countPolls(c, testAddrs, "i1234", "running", status.StatusStarted) 110 c.Assert(count, jc.GreaterThan, 2) 111 } 112 113 // countPolls sets up a machine loop with the given 114 // addresses and status to be returned from getInstanceInfo, 115 // waits for coretesting.ShortWait, and returns the 116 // number of times the instance is polled. 117 func countPolls(c *gc.C, addrs []network.Address, instId, instStatus string, machineStatus status.Status) int { 118 count := int32(0) 119 getInstanceInfo := func(id instance.Id) (instanceInfo, error) { 120 c.Check(string(id), gc.Equals, instId) 121 atomic.AddInt32(&count, 1) 122 if addrs == nil { 123 return instanceInfo{}, fmt.Errorf("no instance addresses available") 124 } 125 return instanceInfo{addrs, instance.InstanceStatus{Status: status.StatusUnknown, Message: instStatus}}, nil 126 } 127 context := &testMachineContext{ 128 getInstanceInfo: getInstanceInfo, 129 dyingc: make(chan struct{}), 130 } 131 m := &testMachine{ 132 tag: names.NewMachineTag("99"), 133 instanceId: instance.Id(instId), 134 refresh: func() error { return nil }, 135 addresses: addrs, 136 life: params.Alive, 137 status: machineStatus, 138 } 139 died := make(chan machine) 140 141 go runMachine(context, m, nil, died) 142 143 time.Sleep(coretesting.ShortWait) 144 killMachineLoop(c, m, context.dyingc, died) 145 c.Assert(context.killErr, gc.Equals, nil) 146 return int(count) 147 } 148 149 func (*machineSuite) TestChangedRefreshes(c *gc.C) { 150 context := &testMachineContext{ 151 getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), 152 dyingc: make(chan struct{}), 153 } 154 refreshc := make(chan struct{}) 155 m := &testMachine{ 156 tag: names.NewMachineTag("99"), 157 instanceId: "i1234", 158 refresh: func() error { 159 refreshc <- struct{}{} 160 return nil 161 }, 162 addresses: testAddrs, 163 life: params.Dead, 164 } 165 died := make(chan machine) 166 changed := make(chan struct{}) 167 go runMachine(context, m, changed, died) 168 select { 169 case <-died: 170 c.Fatalf("machine died prematurely") 171 case <-time.After(coretesting.ShortWait): 172 } 173 174 // Notify the machine that it has changed; it should 175 // refresh, and publish the fact that it no longer has 176 // an address. 177 changed <- struct{}{} 178 179 select { 180 case <-refreshc: 181 case <-time.After(coretesting.LongWait): 182 c.Fatalf("timed out waiting for refresh") 183 } 184 select { 185 case <-died: 186 case <-time.After(coretesting.LongWait): 187 c.Fatalf("expected death after life set to dying") 188 } 189 // The machine addresses should remain the same even 190 // after death. 191 c.Assert(m.addresses, gc.DeepEquals, testAddrs) 192 } 193 194 var terminatingErrorsTests = []struct { 195 about string 196 mutate func(m *testMachine, err error) 197 }{{ 198 about: "set addresses", 199 mutate: func(m *testMachine, err error) { 200 m.setAddressesErr = err 201 }, 202 }, { 203 about: "refresh", 204 mutate: func(m *testMachine, err error) { 205 m.refresh = func() error { 206 return err 207 } 208 }, 209 }, { 210 about: "instance id", 211 mutate: func(m *testMachine, err error) { 212 m.instanceIdErr = err 213 }, 214 }} 215 216 func (*machineSuite) TestTerminatingErrors(c *gc.C) { 217 for i, test := range terminatingErrorsTests { 218 c.Logf("test %d: %s", i, test.about) 219 testTerminatingErrors(c, test.mutate) 220 } 221 } 222 223 // 224 // testTerminatingErrors checks that when a testMachine is 225 // changed with the given mutate function, the machine goroutine 226 // will die having called its context's killAll function with the 227 // given error. The test is cunningly structured so that it in the normal course 228 // of things it will go through all possible places that can return an error. 229 func testTerminatingErrors(c *gc.C, mutate func(m *testMachine, err error)) { 230 context := &testMachineContext{ 231 getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), 232 dyingc: make(chan struct{}), 233 } 234 expectErr := stderrors.New("a very unusual error") 235 m := &testMachine{ 236 tag: names.NewMachineTag("99"), 237 instanceId: "i1234", 238 refresh: func() error { return nil }, 239 life: params.Alive, 240 } 241 mutate(m, expectErr) 242 died := make(chan machine) 243 changed := make(chan struct{}, 1) 244 go runMachine(context, m, changed, died) 245 changed <- struct{}{} 246 select { 247 case <-died: 248 case <-time.After(coretesting.LongWait): 249 c.Fatalf("timed out waiting for machine to die") 250 } 251 c.Assert(context.killErr, gc.ErrorMatches, ".*"+expectErr.Error()) 252 } 253 254 func killMachineLoop(c *gc.C, m machine, dying chan struct{}, died <-chan machine) { 255 close(dying) 256 select { 257 case diedm := <-died: 258 c.Assert(diedm, gc.Equals, m) 259 case <-time.After(coretesting.LongWait): 260 c.Fatalf("updater did not die after dying channel was closed") 261 } 262 } 263 264 func instanceInfoGetter( 265 c *gc.C, expectId instance.Id, addrs []network.Address, 266 instanceStatus string, err error) func(id instance.Id) (instanceInfo, error) { 267 268 return func(id instance.Id) (instanceInfo, error) { 269 c.Check(id, gc.Equals, expectId) 270 return instanceInfo{addrs, instance.InstanceStatus{Status: status.StatusUnknown, Message: instanceStatus}}, err 271 } 272 } 273 274 type testMachineContext struct { 275 killErr error 276 getInstanceInfo func(instance.Id) (instanceInfo, error) 277 dyingc chan struct{} 278 } 279 280 func (context *testMachineContext) kill(err error) { 281 if err == nil { 282 panic("kill with nil error") 283 } 284 context.killErr = err 285 } 286 287 func (context *testMachineContext) instanceInfo(id instance.Id) (instanceInfo, error) { 288 return context.getInstanceInfo(id) 289 } 290 291 func (context *testMachineContext) dying() <-chan struct{} { 292 return context.dyingc 293 } 294 295 func (context *testMachineContext) errDying() error { 296 return nil 297 } 298 299 type testMachine struct { 300 instanceId instance.Id 301 instanceIdErr error 302 tag names.MachineTag 303 instStatus status.Status 304 instStatusInfo string 305 status status.Status 306 refresh func() error 307 setAddressesErr error 308 // mu protects the following fields. 309 mu sync.Mutex 310 life params.Life 311 addresses []network.Address 312 setAddressCount int 313 } 314 315 func (m *testMachine) Tag() names.MachineTag { 316 return m.tag 317 } 318 319 func (m *testMachine) Id() string { 320 return m.tag.Id() 321 } 322 323 func (m *testMachine) ProviderAddresses() ([]network.Address, error) { 324 m.mu.Lock() 325 defer m.mu.Unlock() 326 327 return m.addresses, nil 328 } 329 330 func (m *testMachine) InstanceId() (instance.Id, error) { 331 if m.instanceId == "" { 332 err := ¶ms.Error{ 333 Code: params.CodeNotProvisioned, 334 Message: fmt.Sprintf("machine %v not provisioned", m.Id()), 335 } 336 return "", err 337 } 338 return m.instanceId, m.instanceIdErr 339 } 340 341 // This is stubbed out for testing. 342 var MachineStatus = func(m *testMachine) (params.StatusResult, error) { 343 return params.StatusResult{Status: m.status}, nil 344 } 345 346 func (m *testMachine) Status() (params.StatusResult, error) { 347 return MachineStatus(m) 348 } 349 350 func (m *testMachine) IsManual() (bool, error) { 351 return strings.HasPrefix(string(m.instanceId), "manual:"), nil 352 } 353 354 func (m *testMachine) InstanceStatus() (params.StatusResult, error) { 355 m.mu.Lock() 356 defer m.mu.Unlock() 357 return params.StatusResult{Status: status.Status(m.instStatus)}, nil 358 } 359 360 func (m *testMachine) SetInstanceStatus(machineStatus status.Status, info string, data map[string]interface{}) error { 361 m.mu.Lock() 362 defer m.mu.Unlock() 363 m.instStatus = machineStatus 364 m.instStatusInfo = info 365 return nil 366 } 367 368 func (m *testMachine) SetProviderAddresses(addrs ...network.Address) error { 369 if m.setAddressesErr != nil { 370 return m.setAddressesErr 371 } 372 m.mu.Lock() 373 defer m.mu.Unlock() 374 m.addresses = append(m.addresses[:0], addrs...) 375 m.setAddressCount++ 376 return nil 377 } 378 379 func (m *testMachine) String() string { 380 return m.tag.Id() 381 } 382 383 func (m *testMachine) Refresh() error { 384 return m.refresh() 385 } 386 387 func (m *testMachine) Life() params.Life { 388 m.mu.Lock() 389 defer m.mu.Unlock() 390 return m.life 391 } 392 393 func (m *testMachine) setLife(life params.Life) { 394 m.mu.Lock() 395 defer m.mu.Unlock() 396 m.life = life 397 }