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