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