github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/machiner/machiner_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package machiner_test 5 6 import ( 7 "io/ioutil" 8 "net" 9 "path/filepath" 10 stdtesting "testing" 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/names" 15 gitjujutesting "github.com/juju/testing" 16 jc "github.com/juju/testing/checkers" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/api" 20 apimachiner "github.com/juju/juju/api/machiner" 21 "github.com/juju/juju/apiserver/params" 22 "github.com/juju/juju/juju/testing" 23 "github.com/juju/juju/network" 24 "github.com/juju/juju/state" 25 "github.com/juju/juju/status" 26 coretesting "github.com/juju/juju/testing" 27 "github.com/juju/juju/worker" 28 "github.com/juju/juju/worker/machiner" 29 ) 30 31 func TestPackage(t *stdtesting.T) { 32 coretesting.MgoTestPackage(t) 33 } 34 35 type MachinerSuite struct { 36 coretesting.BaseSuite 37 accessor *mockMachineAccessor 38 machineTag names.MachineTag 39 addresses []net.Addr 40 } 41 42 var _ = gc.Suite(&MachinerSuite{}) 43 44 func (s *MachinerSuite) SetUpTest(c *gc.C) { 45 s.BaseSuite.SetUpTest(c) 46 s.accessor = &mockMachineAccessor{} 47 s.accessor.machine.watcher.changes = make(chan struct{}) 48 s.accessor.machine.life = params.Alive 49 s.machineTag = names.NewMachineTag("123") 50 s.addresses = []net.Addr{ // anything will do 51 &net.IPAddr{IP: net.IPv4bcast}, 52 &net.IPAddr{IP: net.IPv4zero}, 53 } 54 s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) { 55 return s.addresses, nil 56 }) 57 s.PatchValue(machiner.GetObservedNetworkConfig, func() ([]params.NetworkConfig, error) { 58 return nil, nil 59 }) 60 } 61 62 func (s *MachinerSuite) TestMachinerConfigValidate(c *gc.C) { 63 _, err := machiner.NewMachiner(machiner.Config{}) 64 c.Assert(err, gc.ErrorMatches, "validating config: unspecified MachineAccessor not valid") 65 _, err = machiner.NewMachiner(machiner.Config{ 66 MachineAccessor: &mockMachineAccessor{}, 67 }) 68 c.Assert(err, gc.ErrorMatches, "validating config: unspecified Tag not valid") 69 70 w, err := machiner.NewMachiner(machiner.Config{ 71 MachineAccessor: &mockMachineAccessor{}, 72 Tag: names.NewMachineTag("123"), 73 }) 74 c.Assert(err, jc.ErrorIsNil) 75 76 // must stop the worker to prevent a data race when cleanup suite 77 // rolls back the patches 78 err = stopWorker(w) 79 c.Assert(err, jc.ErrorIsNil) 80 } 81 82 func (s *MachinerSuite) TestMachinerMachineNotFound(c *gc.C) { 83 // Accessing the machine initially yields "not found or unauthorized". 84 // We don't know which, so we don't report that the machine is dead. 85 var machineDead machineDeathTracker 86 w, err := machiner.NewMachiner(machiner.Config{ 87 s.accessor, s.machineTag, false, 88 machineDead.machineDead, 89 }) 90 c.Assert(err, jc.ErrorIsNil) 91 s.accessor.machine.SetErrors( 92 nil, // SetMachineAddresses 93 nil, // SetStatus 94 nil, // Watch 95 ¶ms.Error{Code: params.CodeNotFound}, // Refresh 96 ) 97 s.accessor.machine.watcher.changes <- struct{}{} 98 err = stopWorker(w) 99 c.Assert(errors.Cause(err), gc.Equals, worker.ErrTerminateAgent) 100 c.Assert(bool(machineDead), jc.IsFalse) 101 } 102 103 func (s *MachinerSuite) TestMachinerSetStatusStopped(c *gc.C) { 104 w, err := machiner.NewMachiner(machiner.Config{ 105 MachineAccessor: s.accessor, 106 Tag: s.machineTag, 107 }) 108 c.Assert(err, jc.ErrorIsNil) 109 s.accessor.machine.life = params.Dying 110 s.accessor.machine.SetErrors( 111 nil, // SetMachineAddresses 112 nil, // SetStatus (started) 113 nil, // Watch 114 nil, // Refresh 115 errors.New("cannot set status"), // SetStatus (stopped) 116 ) 117 s.accessor.machine.watcher.changes <- struct{}{} 118 err = stopWorker(w) 119 c.Assert( 120 err, gc.ErrorMatches, 121 "machine-123 failed to set status stopped: cannot set status", 122 ) 123 s.accessor.machine.CheckCallNames(c, 124 "SetMachineAddresses", 125 "SetStatus", 126 "Watch", 127 "Refresh", 128 "Life", 129 "SetStatus", 130 ) 131 s.accessor.machine.CheckCall( 132 c, 5, "SetStatus", 133 status.StatusStopped, 134 "", 135 map[string]interface{}(nil), 136 ) 137 } 138 139 func (s *MachinerSuite) TestMachinerMachineEnsureDeadError(c *gc.C) { 140 w, err := machiner.NewMachiner(machiner.Config{ 141 MachineAccessor: s.accessor, 142 Tag: s.machineTag, 143 }) 144 c.Assert(err, jc.ErrorIsNil) 145 s.accessor.machine.life = params.Dying 146 s.accessor.machine.SetErrors( 147 nil, // SetMachineAddresses 148 nil, // SetStatus 149 nil, // Watch 150 nil, // Refresh 151 nil, // SetStatus 152 errors.New("cannot ensure machine is dead"), // EnsureDead 153 ) 154 s.accessor.machine.watcher.changes <- struct{}{} 155 err = stopWorker(w) 156 c.Check( 157 err, gc.ErrorMatches, 158 "machine-123 failed to set machine to dead: cannot ensure machine is dead", 159 ) 160 } 161 162 func (s *MachinerSuite) TestMachinerMachineAssignedUnits(c *gc.C) { 163 w, err := machiner.NewMachiner(machiner.Config{ 164 MachineAccessor: s.accessor, 165 Tag: s.machineTag, 166 }) 167 c.Assert(err, jc.ErrorIsNil) 168 s.accessor.machine.life = params.Dying 169 s.accessor.machine.SetErrors( 170 nil, // SetMachineAddresses 171 nil, // SetStatus 172 nil, // Watch 173 nil, // Refresh 174 nil, // SetStatus 175 ¶ms.Error{Code: params.CodeHasAssignedUnits}, // EnsureDead 176 ) 177 s.accessor.machine.watcher.changes <- struct{}{} 178 err = stopWorker(w) 179 180 // If EnsureDead fails with "machine has assigned units", then 181 // the worker will not fail, but will wait for more events. 182 c.Check(err, jc.ErrorIsNil) 183 184 s.accessor.machine.CheckCallNames(c, 185 "SetMachineAddresses", 186 "SetStatus", 187 "Watch", 188 "Refresh", 189 "Life", 190 "SetStatus", 191 "EnsureDead", 192 ) 193 } 194 195 func (s *MachinerSuite) TestMachinerStorageAttached(c *gc.C) { 196 // Machine is dying. We'll respond to "EnsureDead" by 197 // saying that there are still storage attachments; 198 // this should not cause an error. 199 s.accessor.machine.life = params.Dying 200 s.accessor.machine.SetErrors( 201 nil, // SetMachineAddresses 202 nil, // SetStatus 203 nil, // Watch 204 nil, // Refresh 205 nil, // SetStatus 206 ¶ms.Error{Code: params.CodeMachineHasAttachedStorage}, 207 ) 208 209 worker, err := machiner.NewMachiner(machiner.Config{ 210 s.accessor, s.machineTag, false, 211 func() error { return nil }, 212 }) 213 c.Assert(err, jc.ErrorIsNil) 214 s.accessor.machine.watcher.changes <- struct{}{} 215 err = stopWorker(worker) 216 c.Check(err, jc.ErrorIsNil) 217 218 s.accessor.CheckCalls(c, []gitjujutesting.StubCall{{ 219 FuncName: "Machine", 220 Args: []interface{}{s.machineTag}, 221 }}) 222 223 s.accessor.machine.CheckCalls(c, []gitjujutesting.StubCall{{ 224 FuncName: "SetMachineAddresses", 225 Args: []interface{}{ 226 network.NewAddresses( 227 "255.255.255.255", 228 "0.0.0.0", 229 ), 230 }, 231 }, { 232 FuncName: "SetStatus", 233 Args: []interface{}{ 234 status.StatusStarted, 235 "", 236 map[string]interface{}(nil), 237 }, 238 }, { 239 FuncName: "Watch", 240 }, { 241 FuncName: "Refresh", 242 }, { 243 FuncName: "Life", 244 }, { 245 FuncName: "SetStatus", 246 Args: []interface{}{ 247 status.StatusStopped, 248 "", 249 map[string]interface{}(nil), 250 }, 251 }, { 252 FuncName: "EnsureDead", 253 }}) 254 } 255 256 // worstCase is used for timeouts when timing out 257 // will fail the test. Raising this value should 258 // not affect the overall running time of the tests 259 // unless they fail. 260 const worstCase = 5 * time.Second 261 262 type MachinerStateSuite struct { 263 testing.JujuConnSuite 264 265 st api.Connection 266 machinerState *apimachiner.State 267 machine *state.Machine 268 apiMachine *apimachiner.Machine 269 } 270 271 var _ = gc.Suite(&MachinerStateSuite{}) 272 273 func (s *MachinerStateSuite) SetUpTest(c *gc.C) { 274 s.JujuConnSuite.SetUpTest(c) 275 s.st, s.machine = s.OpenAPIAsNewMachine(c) 276 277 // Create the machiner API facade. 278 s.machinerState = apimachiner.NewState(s.st) 279 c.Assert(s.machinerState, gc.NotNil) 280 281 // Get the machine through the facade. 282 var err error 283 s.apiMachine, err = s.machinerState.Machine(s.machine.Tag().(names.MachineTag)) 284 c.Assert(err, jc.ErrorIsNil) 285 c.Assert(s.apiMachine.Tag(), gc.Equals, s.machine.Tag()) 286 // Isolate tests better by not using real interface addresses. 287 s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) { 288 return nil, nil 289 }) 290 s.PatchValue(&network.InterfaceByNameAddrs, func(string) ([]net.Addr, error) { 291 return nil, nil 292 }) 293 s.PatchValue(&network.LXCNetDefaultConfig, "") 294 s.PatchValue(machiner.GetObservedNetworkConfig, func() ([]params.NetworkConfig, error) { 295 return nil, nil 296 }) 297 } 298 299 func (s *MachinerStateSuite) waitMachineStatus(c *gc.C, m *state.Machine, expectStatus status.Status) { 300 timeout := time.After(worstCase) 301 for { 302 select { 303 case <-timeout: 304 c.Fatalf("timeout while waiting for machine status to change") 305 case <-time.After(10 * time.Millisecond): 306 statusInfo, err := m.Status() 307 c.Assert(err, jc.ErrorIsNil) 308 if statusInfo.Status != expectStatus { 309 c.Logf("machine %q status is %s, still waiting", m, statusInfo.Status) 310 continue 311 } 312 return 313 } 314 } 315 } 316 317 func (s *MachinerStateSuite) TestNotFoundOrUnauthorized(c *gc.C) { 318 mr, err := machiner.NewMachiner(machiner.Config{ 319 machiner.APIMachineAccessor{s.machinerState}, 320 names.NewMachineTag("99"), 321 false, 322 // the "machineDead" callback should not be invoked 323 // because we don't know whether the agent is 324 // legimitately not found or unauthorized; we err on 325 // the side of caution, in case the password got mucked 326 // up, or state got mucked up (e.g. during an upgrade). 327 func() error { return errors.New("should not be called") }, 328 }) 329 c.Assert(err, jc.ErrorIsNil) 330 c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent) 331 } 332 333 func (s *MachinerStateSuite) makeMachiner( 334 c *gc.C, 335 ignoreAddresses bool, 336 machineDead func() error, 337 ) worker.Worker { 338 if machineDead == nil { 339 machineDead = func() error { return nil } 340 } 341 w, err := machiner.NewMachiner(machiner.Config{ 342 machiner.APIMachineAccessor{s.machinerState}, 343 s.apiMachine.Tag().(names.MachineTag), 344 ignoreAddresses, 345 machineDead, 346 }) 347 c.Assert(err, jc.ErrorIsNil) 348 return w 349 } 350 351 type machineDeathTracker bool 352 353 func (t *machineDeathTracker) machineDead() error { 354 *t = true 355 return nil 356 } 357 358 func (s *MachinerStateSuite) TestRunStop(c *gc.C) { 359 var machineDead machineDeathTracker 360 mr := s.makeMachiner(c, false, machineDead.machineDead) 361 c.Assert(worker.Stop(mr), jc.ErrorIsNil) 362 c.Assert(s.apiMachine.Refresh(), jc.ErrorIsNil) 363 c.Assert(s.apiMachine.Life(), gc.Equals, params.Alive) 364 c.Assert(bool(machineDead), jc.IsFalse) 365 } 366 367 func (s *MachinerStateSuite) TestStartSetsStatus(c *gc.C) { 368 statusInfo, err := s.machine.Status() 369 c.Assert(err, jc.ErrorIsNil) 370 c.Assert(statusInfo.Status, gc.Equals, status.StatusPending) 371 c.Assert(statusInfo.Message, gc.Equals, "") 372 373 mr := s.makeMachiner(c, false, nil) 374 defer worker.Stop(mr) 375 376 s.waitMachineStatus(c, s.machine, status.StatusStarted) 377 } 378 379 func (s *MachinerStateSuite) TestSetsStatusWhenDying(c *gc.C) { 380 mr := s.makeMachiner(c, false, nil) 381 defer worker.Stop(mr) 382 c.Assert(s.machine.Destroy(), jc.ErrorIsNil) 383 s.waitMachineStatus(c, s.machine, status.StatusStopped) 384 } 385 386 func (s *MachinerStateSuite) TestSetDead(c *gc.C) { 387 var machineDead machineDeathTracker 388 mr := s.makeMachiner(c, false, machineDead.machineDead) 389 defer worker.Stop(mr) 390 c.Assert(s.machine.Destroy(), jc.ErrorIsNil) 391 s.State.StartSync() 392 c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent) 393 c.Assert(s.machine.Refresh(), jc.ErrorIsNil) 394 c.Assert(s.machine.Life(), gc.Equals, state.Dead) 395 c.Assert(bool(machineDead), jc.IsTrue) 396 } 397 398 func (s *MachinerStateSuite) TestSetDeadWithDyingUnit(c *gc.C) { 399 var machineDead machineDeathTracker 400 mr := s.makeMachiner(c, false, machineDead.machineDead) 401 defer worker.Stop(mr) 402 403 // Add a service, assign to machine. 404 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 405 unit, err := wordpress.AddUnit() 406 c.Assert(err, jc.ErrorIsNil) 407 err = unit.AssignToMachine(s.machine) 408 c.Assert(err, jc.ErrorIsNil) 409 410 // Service alive, can't destroy machine. 411 err = s.machine.Destroy() 412 c.Assert(err, jc.Satisfies, state.IsHasAssignedUnitsError) 413 414 err = wordpress.Destroy() 415 c.Assert(err, jc.ErrorIsNil) 416 417 // With dying unit, machine can now be marked as dying. 418 c.Assert(s.machine.Destroy(), jc.ErrorIsNil) 419 s.State.StartSync() 420 c.Assert(s.machine.Refresh(), jc.ErrorIsNil) 421 c.Assert(s.machine.Life(), gc.Equals, state.Dying) 422 c.Assert(bool(machineDead), jc.IsFalse) 423 424 // When the unit is ultimately destroyed, the machine becomes dead. 425 err = unit.Destroy() 426 c.Assert(err, jc.ErrorIsNil) 427 s.State.StartSync() 428 c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent) 429 c.Assert(bool(machineDead), jc.IsTrue) 430 431 } 432 433 func (s *MachinerStateSuite) setupSetMachineAddresses(c *gc.C, ignore bool) { 434 lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net") 435 netConf := []byte(` 436 # comments ignored 437 LXC_BR= ignored 438 LXC_ADDR = "fooo" 439 LXC_BRIDGE="foobar" # detected 440 anything else ignored 441 LXC_BRIDGE="ignored"`[1:]) 442 err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644) 443 c.Assert(err, jc.ErrorIsNil) 444 s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) { 445 addrs := []net.Addr{ 446 &net.IPAddr{IP: net.IPv4(10, 0, 0, 1)}, 447 &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}, 448 &net.IPAddr{IP: net.IPv4(10, 0, 3, 1)}, // lxc bridge address ignored 449 &net.IPAddr{IP: net.IPv6loopback}, 450 &net.UnixAddr{}, // not IP, ignored 451 &net.IPAddr{IP: net.IPv4(10, 0, 3, 4)}, // lxc bridge address ignored 452 &net.IPNet{IP: net.ParseIP("2001:db8::1")}, 453 &net.IPAddr{IP: net.IPv4(169, 254, 1, 20)}, // LinkLocal Ignored 454 &net.IPNet{IP: net.ParseIP("fe80::1")}, // LinkLocal Ignored 455 } 456 return addrs, nil 457 }) 458 s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) { 459 c.Assert(name, gc.Equals, "foobar") 460 return []net.Addr{ 461 &net.IPAddr{IP: net.IPv4(10, 0, 3, 1)}, 462 &net.IPAddr{IP: net.IPv4(10, 0, 3, 4)}, 463 }, nil 464 }) 465 s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig) 466 467 mr := s.makeMachiner(c, ignore, nil) 468 defer worker.Stop(mr) 469 c.Assert(s.machine.Destroy(), jc.ErrorIsNil) 470 s.State.StartSync() 471 c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent) 472 c.Assert(s.machine.Refresh(), jc.ErrorIsNil) 473 } 474 475 func (s *MachinerStateSuite) TestMachineAddresses(c *gc.C) { 476 s.setupSetMachineAddresses(c, false) 477 c.Assert(s.machine.MachineAddresses(), jc.SameContents, []network.Address{ 478 network.NewAddress("2001:db8::1"), 479 network.NewScopedAddress("10.0.0.1", network.ScopeCloudLocal), 480 network.NewScopedAddress("::1", network.ScopeMachineLocal), 481 network.NewScopedAddress("127.0.0.1", network.ScopeMachineLocal), 482 }) 483 } 484 485 func (s *MachinerStateSuite) TestMachineAddressesWithIgnoreFlag(c *gc.C) { 486 s.setupSetMachineAddresses(c, true) 487 c.Assert(s.machine.MachineAddresses(), gc.HasLen, 0) 488 } 489 490 func stopWorker(w worker.Worker) error { 491 w.Kill() 492 return w.Wait() 493 }