github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/unit_test.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "strconv" 8 9 "github.com/juju/errors" 10 jc "github.com/juju/testing/checkers" 11 jujutxn "github.com/juju/txn" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/charm.v4" 14 15 "github.com/juju/juju/instance" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/state/testing" 19 coretesting "github.com/juju/juju/testing" 20 ) 21 22 const ( 23 contentionErr = ".*: state changing too quickly; try again soon" 24 ) 25 26 type UnitSuite struct { 27 ConnSuite 28 charm *state.Charm 29 service *state.Service 30 unit *state.Unit 31 } 32 33 var _ = gc.Suite(&UnitSuite{}) 34 35 func (s *UnitSuite) SetUpTest(c *gc.C) { 36 s.ConnSuite.SetUpTest(c) 37 s.charm = s.AddTestingCharm(c, "wordpress") 38 var err error 39 s.service = s.AddTestingService(c, "wordpress", s.charm) 40 c.Assert(err, jc.ErrorIsNil) 41 s.unit, err = s.service.AddUnit() 42 c.Assert(err, jc.ErrorIsNil) 43 c.Assert(s.unit.Series(), gc.Equals, "quantal") 44 } 45 46 func (s *UnitSuite) TestUnitNotFound(c *gc.C) { 47 _, err := s.State.Unit("subway/0") 48 c.Assert(err, gc.ErrorMatches, `unit "subway/0" not found`) 49 c.Assert(err, jc.Satisfies, errors.IsNotFound) 50 } 51 52 func (s *UnitSuite) TestService(c *gc.C) { 53 svc, err := s.unit.Service() 54 c.Assert(err, jc.ErrorIsNil) 55 c.Assert(svc.Name(), gc.Equals, s.unit.ServiceName()) 56 } 57 58 func (s *UnitSuite) TestConfigSettingsNeedCharmURLSet(c *gc.C) { 59 _, err := s.unit.ConfigSettings() 60 c.Assert(err, gc.ErrorMatches, "unit charm not set") 61 } 62 63 func (s *UnitSuite) TestConfigSettingsIncludeDefaults(c *gc.C) { 64 err := s.unit.SetCharmURL(s.charm.URL()) 65 c.Assert(err, jc.ErrorIsNil) 66 settings, err := s.unit.ConfigSettings() 67 c.Assert(err, jc.ErrorIsNil) 68 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 69 } 70 71 func (s *UnitSuite) TestConfigSettingsReflectService(c *gc.C) { 72 err := s.service.UpdateConfigSettings(charm.Settings{"blog-title": "no title"}) 73 c.Assert(err, jc.ErrorIsNil) 74 err = s.unit.SetCharmURL(s.charm.URL()) 75 c.Assert(err, jc.ErrorIsNil) 76 settings, err := s.unit.ConfigSettings() 77 c.Assert(err, jc.ErrorIsNil) 78 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "no title"}) 79 80 err = s.service.UpdateConfigSettings(charm.Settings{"blog-title": "ironic title"}) 81 c.Assert(err, jc.ErrorIsNil) 82 settings, err = s.unit.ConfigSettings() 83 c.Assert(err, jc.ErrorIsNil) 84 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "ironic title"}) 85 } 86 87 func (s *UnitSuite) TestConfigSettingsReflectCharm(c *gc.C) { 88 err := s.unit.SetCharmURL(s.charm.URL()) 89 c.Assert(err, jc.ErrorIsNil) 90 newCharm := s.AddConfigCharm(c, "wordpress", "options: {}", 123) 91 err = s.service.SetCharm(newCharm, false) 92 c.Assert(err, jc.ErrorIsNil) 93 94 // Settings still reflect charm set on unit. 95 settings, err := s.unit.ConfigSettings() 96 c.Assert(err, jc.ErrorIsNil) 97 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 98 99 // When the unit has the new charm set, it'll see the new config. 100 err = s.unit.SetCharmURL(newCharm.URL()) 101 c.Assert(err, jc.ErrorIsNil) 102 settings, err = s.unit.ConfigSettings() 103 c.Assert(err, jc.ErrorIsNil) 104 c.Assert(settings, gc.DeepEquals, charm.Settings{}) 105 } 106 107 func (s *UnitSuite) TestWatchConfigSettingsNeedsCharmURL(c *gc.C) { 108 _, err := s.unit.WatchConfigSettings() 109 c.Assert(err, gc.ErrorMatches, "unit charm not set") 110 } 111 112 func (s *UnitSuite) TestWatchConfigSettings(c *gc.C) { 113 err := s.unit.SetCharmURL(s.charm.URL()) 114 c.Assert(err, jc.ErrorIsNil) 115 w, err := s.unit.WatchConfigSettings() 116 c.Assert(err, jc.ErrorIsNil) 117 defer testing.AssertStop(c, w) 118 119 // Initial event. 120 wc := testing.NewNotifyWatcherC(c, s.State, w) 121 wc.AssertOneChange() 122 123 // Update config a couple of times, check a single event. 124 err = s.service.UpdateConfigSettings(charm.Settings{ 125 "blog-title": "superhero paparazzi", 126 }) 127 c.Assert(err, jc.ErrorIsNil) 128 err = s.service.UpdateConfigSettings(charm.Settings{ 129 "blog-title": "sauceror central", 130 }) 131 c.Assert(err, jc.ErrorIsNil) 132 wc.AssertOneChange() 133 134 // Non-change is not reported. 135 err = s.service.UpdateConfigSettings(charm.Settings{ 136 "blog-title": "sauceror central", 137 }) 138 c.Assert(err, jc.ErrorIsNil) 139 wc.AssertNoChange() 140 141 // Change service's charm; nothing detected. 142 newCharm := s.AddConfigCharm(c, "wordpress", floatConfig, 123) 143 err = s.service.SetCharm(newCharm, false) 144 c.Assert(err, jc.ErrorIsNil) 145 wc.AssertNoChange() 146 147 // Change service config for new charm; nothing detected. 148 err = s.service.UpdateConfigSettings(charm.Settings{ 149 "key": 42.0, 150 }) 151 c.Assert(err, jc.ErrorIsNil) 152 wc.AssertNoChange() 153 154 // NOTE: if we were to change the unit to use the new charm, we'd see 155 // another event, because the originally-watched document will become 156 // unreferenced and be removed. But I'm not testing that behaviour 157 // because it's not very helpful and subject to change. 158 } 159 160 func (s *UnitSuite) addSubordinateUnit(c *gc.C) *state.Unit { 161 subCharm := s.AddTestingCharm(c, "logging") 162 s.AddTestingService(c, "logging", subCharm) 163 eps, err := s.State.InferEndpoints("wordpress", "logging") 164 c.Assert(err, jc.ErrorIsNil) 165 rel, err := s.State.AddRelation(eps...) 166 c.Assert(err, jc.ErrorIsNil) 167 ru, err := rel.Unit(s.unit) 168 c.Assert(err, jc.ErrorIsNil) 169 err = ru.EnterScope(nil) 170 c.Assert(err, jc.ErrorIsNil) 171 subUnit, err := s.State.Unit("logging/0") 172 c.Assert(err, jc.ErrorIsNil) 173 return subUnit 174 } 175 176 func (s *UnitSuite) setAssignedMachineAddresses(c *gc.C, u *state.Unit) { 177 err := u.AssignToNewMachine() 178 c.Assert(err, jc.ErrorIsNil) 179 mid, err := u.AssignedMachineId() 180 c.Assert(err, jc.ErrorIsNil) 181 machine, err := s.State.Machine(mid) 182 c.Assert(err, jc.ErrorIsNil) 183 err = machine.SetProvisioned("i-exist", "fake_nonce", nil) 184 c.Assert(err, jc.ErrorIsNil) 185 err = machine.SetAddresses(network.Address{ 186 Type: network.IPv4Address, 187 Scope: network.ScopeCloudLocal, 188 Value: "private.address.example.com", 189 }, network.Address{ 190 Type: network.IPv4Address, 191 Scope: network.ScopePublic, 192 Value: "public.address.example.com", 193 }) 194 c.Assert(err, jc.ErrorIsNil) 195 } 196 197 func (s *UnitSuite) TestPublicAddressSubordinate(c *gc.C) { 198 subUnit := s.addSubordinateUnit(c) 199 _, ok := subUnit.PublicAddress() 200 c.Assert(ok, jc.IsFalse) 201 202 s.setAssignedMachineAddresses(c, s.unit) 203 address, ok := subUnit.PublicAddress() 204 c.Assert(ok, jc.IsTrue) 205 c.Assert(address, gc.Equals, "public.address.example.com") 206 } 207 208 func (s *UnitSuite) TestPublicAddress(c *gc.C) { 209 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 210 c.Assert(err, jc.ErrorIsNil) 211 err = s.unit.AssignToMachine(machine) 212 c.Assert(err, jc.ErrorIsNil) 213 214 address, ok := s.unit.PublicAddress() 215 c.Check(address, gc.Equals, "") 216 c.Assert(ok, jc.IsFalse) 217 218 public := network.NewAddress("8.8.8.8", network.ScopePublic) 219 private := network.NewAddress("127.0.0.1", network.ScopeCloudLocal) 220 221 err = machine.SetAddresses(public, private) 222 c.Assert(err, jc.ErrorIsNil) 223 224 address, ok = s.unit.PublicAddress() 225 c.Check(address, gc.Equals, "8.8.8.8") 226 c.Assert(ok, jc.IsTrue) 227 } 228 229 func (s *UnitSuite) TestPublicAddressMachineAddresses(c *gc.C) { 230 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 231 c.Assert(err, jc.ErrorIsNil) 232 err = s.unit.AssignToMachine(machine) 233 c.Assert(err, jc.ErrorIsNil) 234 235 publicProvider := network.NewAddress("8.8.8.8", network.ScopePublic) 236 privateProvider := network.NewAddress("127.0.0.1", network.ScopeCloudLocal) 237 privateMachine := network.NewAddress("127.0.0.2", network.ScopeUnknown) 238 239 err = machine.SetAddresses(privateProvider) 240 c.Assert(err, jc.ErrorIsNil) 241 err = machine.SetMachineAddresses(privateMachine) 242 c.Assert(err, jc.ErrorIsNil) 243 address, ok := s.unit.PublicAddress() 244 c.Check(address, gc.Equals, "127.0.0.1") 245 c.Assert(ok, jc.IsTrue) 246 247 err = machine.SetAddresses(publicProvider, privateProvider) 248 c.Assert(err, jc.ErrorIsNil) 249 address, ok = s.unit.PublicAddress() 250 c.Check(address, gc.Equals, "8.8.8.8") 251 c.Assert(ok, jc.IsTrue) 252 } 253 254 func (s *UnitSuite) TestPrivateAddressSubordinate(c *gc.C) { 255 subUnit := s.addSubordinateUnit(c) 256 _, ok := subUnit.PrivateAddress() 257 c.Assert(ok, jc.IsFalse) 258 259 s.setAssignedMachineAddresses(c, s.unit) 260 address, ok := subUnit.PrivateAddress() 261 c.Assert(ok, jc.IsTrue) 262 c.Assert(address, gc.Equals, "private.address.example.com") 263 } 264 265 func (s *UnitSuite) TestPrivateAddress(c *gc.C) { 266 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 267 c.Assert(err, jc.ErrorIsNil) 268 err = s.unit.AssignToMachine(machine) 269 c.Assert(err, jc.ErrorIsNil) 270 271 address, ok := s.unit.PrivateAddress() 272 c.Check(address, gc.Equals, "") 273 c.Assert(ok, jc.IsFalse) 274 275 public := network.NewAddress("8.8.8.8", network.ScopePublic) 276 private := network.NewAddress("127.0.0.1", network.ScopeCloudLocal) 277 278 err = machine.SetAddresses(public, private) 279 c.Assert(err, jc.ErrorIsNil) 280 281 address, ok = s.unit.PrivateAddress() 282 c.Check(address, gc.Equals, "127.0.0.1") 283 c.Assert(ok, jc.IsTrue) 284 } 285 286 type destroyMachineTestCase struct { 287 target *state.Unit 288 host *state.Machine 289 desc string 290 flipHook []jujutxn.TestHook 291 destroyed bool 292 } 293 294 func (s *UnitSuite) destroyMachineTestCases(c *gc.C) []destroyMachineTestCase { 295 var result []destroyMachineTestCase 296 var err error 297 298 { 299 tc := destroyMachineTestCase{desc: "standalone principal", destroyed: true} 300 tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits) 301 c.Assert(err, jc.ErrorIsNil) 302 tc.target, err = s.service.AddUnit() 303 c.Assert(err, jc.ErrorIsNil) 304 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 305 result = append(result, tc) 306 } 307 { 308 tc := destroyMachineTestCase{desc: "co-located principals", destroyed: false} 309 tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits) 310 c.Assert(err, jc.ErrorIsNil) 311 tc.target, err = s.service.AddUnit() 312 c.Assert(err, jc.ErrorIsNil) 313 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 314 colocated, err := s.service.AddUnit() 315 c.Assert(err, jc.ErrorIsNil) 316 c.Assert(colocated.AssignToMachine(tc.host), gc.IsNil) 317 318 result = append(result, tc) 319 } 320 { 321 tc := destroyMachineTestCase{desc: "host has container", destroyed: false} 322 tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits) 323 c.Assert(err, jc.ErrorIsNil) 324 _, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 325 Series: "quantal", 326 Jobs: []state.MachineJob{state.JobHostUnits}, 327 }, tc.host.Id(), instance.LXC) 328 c.Assert(err, jc.ErrorIsNil) 329 tc.target, err = s.service.AddUnit() 330 c.Assert(err, jc.ErrorIsNil) 331 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 332 333 result = append(result, tc) 334 } 335 { 336 tc := destroyMachineTestCase{desc: "host has vote", destroyed: false} 337 tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits) 338 c.Assert(err, jc.ErrorIsNil) 339 c.Assert(tc.host.SetHasVote(true), gc.IsNil) 340 tc.target, err = s.service.AddUnit() 341 c.Assert(err, jc.ErrorIsNil) 342 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 343 344 result = append(result, tc) 345 } 346 { 347 tc := destroyMachineTestCase{desc: "unassigned unit", destroyed: true} 348 tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits) 349 c.Assert(err, jc.ErrorIsNil) 350 tc.target, err = s.service.AddUnit() 351 c.Assert(err, jc.ErrorIsNil) 352 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 353 result = append(result, tc) 354 } 355 356 return result 357 } 358 359 func (s *UnitSuite) TestRemoveUnitMachineFastForwardDestroy(c *gc.C) { 360 for _, tc := range s.destroyMachineTestCases(c) { 361 c.Log(tc.desc) 362 c.Assert(tc.target.Destroy(), gc.IsNil) 363 if tc.destroyed { 364 assertLife(c, tc.host, state.Dying) 365 c.Assert(tc.host.EnsureDead(), gc.IsNil) 366 } else { 367 assertLife(c, tc.host, state.Alive) 368 c.Assert(tc.host.Destroy(), gc.NotNil) 369 } 370 } 371 } 372 373 func (s *UnitSuite) TestRemoveUnitMachineNoFastForwardDestroy(c *gc.C) { 374 for _, tc := range s.destroyMachineTestCases(c) { 375 c.Log(tc.desc) 376 preventUnitDestroyRemove(c, tc.target) 377 c.Assert(tc.target.Destroy(), gc.IsNil) 378 c.Assert(tc.target.EnsureDead(), gc.IsNil) 379 assertLife(c, tc.host, state.Alive) 380 c.Assert(tc.target.Remove(), gc.IsNil) 381 if tc.destroyed { 382 assertLife(c, tc.host, state.Dying) 383 } else { 384 assertLife(c, tc.host, state.Alive) 385 c.Assert(tc.host.Destroy(), gc.NotNil) 386 } 387 } 388 } 389 390 func (s *UnitSuite) setMachineVote(c *gc.C, id string, hasVote bool) { 391 m, err := s.State.Machine(id) 392 c.Assert(err, jc.ErrorIsNil) 393 c.Assert(m.SetHasVote(hasVote), gc.IsNil) 394 } 395 396 func (s *UnitSuite) TestRemoveUnitMachineThrashed(c *gc.C) { 397 host, err := s.State.AddMachine("quantal", state.JobHostUnits) 398 c.Assert(err, jc.ErrorIsNil) 399 target, err := s.service.AddUnit() 400 c.Assert(err, jc.ErrorIsNil) 401 c.Assert(target.AssignToMachine(host), gc.IsNil) 402 flip := jujutxn.TestHook{ 403 Before: func() { 404 s.setMachineVote(c, host.Id(), true) 405 }, 406 } 407 flop := jujutxn.TestHook{ 408 Before: func() { 409 s.setMachineVote(c, host.Id(), false) 410 }, 411 } 412 // You'll need to adjust the flip-flops to match the number of transaction 413 // retries. 414 defer state.SetTestHooks(c, s.State, flip, flop, flip).Check() 415 416 c.Assert(target.Destroy(), gc.ErrorMatches, "state changing too quickly; try again soon") 417 } 418 419 func (s *UnitSuite) TestRemoveUnitMachineRetryVoter(c *gc.C) { 420 host, err := s.State.AddMachine("quantal", state.JobHostUnits) 421 c.Assert(err, jc.ErrorIsNil) 422 target, err := s.service.AddUnit() 423 c.Assert(err, jc.ErrorIsNil) 424 c.Assert(target.AssignToMachine(host), gc.IsNil) 425 426 defer state.SetBeforeHooks(c, s.State, func() { 427 s.setMachineVote(c, host.Id(), true) 428 }, nil).Check() 429 430 c.Assert(target.Destroy(), gc.IsNil) 431 assertLife(c, host, state.Alive) 432 } 433 434 func (s *UnitSuite) TestRemoveUnitMachineRetryNoVoter(c *gc.C) { 435 host, err := s.State.AddMachine("quantal", state.JobHostUnits) 436 c.Assert(err, jc.ErrorIsNil) 437 target, err := s.service.AddUnit() 438 c.Assert(err, jc.ErrorIsNil) 439 c.Assert(target.AssignToMachine(host), gc.IsNil) 440 c.Assert(host.SetHasVote(true), gc.IsNil) 441 442 defer state.SetBeforeHooks(c, s.State, func() { 443 s.setMachineVote(c, host.Id(), false) 444 }, nil).Check() 445 446 c.Assert(target.Destroy(), gc.IsNil) 447 assertLife(c, host, state.Dying) 448 } 449 450 func (s *UnitSuite) TestRemoveUnitMachineRetryContainer(c *gc.C) { 451 host, err := s.State.AddMachine("quantal", state.JobHostUnits) 452 c.Assert(err, jc.ErrorIsNil) 453 target, err := s.service.AddUnit() 454 c.Assert(err, jc.ErrorIsNil) 455 c.Assert(target.AssignToMachine(host), gc.IsNil) 456 defer state.SetTestHooks(c, s.State, jujutxn.TestHook{ 457 Before: func() { 458 machine, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 459 Series: "quantal", 460 Jobs: []state.MachineJob{state.JobHostUnits}, 461 }, host.Id(), instance.LXC) 462 c.Assert(err, jc.ErrorIsNil) 463 assertLife(c, machine, state.Alive) 464 465 // test-setup verification for the disqualifying machine. 466 hostHandle, err := s.State.Machine(host.Id()) 467 c.Assert(err, jc.ErrorIsNil) 468 containers, err := hostHandle.Containers() 469 c.Assert(err, jc.ErrorIsNil) 470 c.Assert(containers, gc.HasLen, 1) 471 }, 472 }).Check() 473 474 c.Assert(target.Destroy(), gc.IsNil) 475 assertLife(c, host, state.Alive) 476 } 477 478 func (s *UnitSuite) TestRemoveUnitMachineRetryOrCond(c *gc.C) { 479 host, err := s.State.AddMachine("quantal", state.JobHostUnits) 480 c.Assert(err, jc.ErrorIsNil) 481 target, err := s.service.AddUnit() 482 c.Assert(err, jc.ErrorIsNil) 483 c.Assert(target.AssignToMachine(host), gc.IsNil) 484 485 // This unit will be colocated in the transaction hook to cause a retry. 486 colocated, err := s.service.AddUnit() 487 c.Assert(err, jc.ErrorIsNil) 488 489 c.Assert(host.SetHasVote(true), gc.IsNil) 490 491 defer state.SetTestHooks(c, s.State, jujutxn.TestHook{ 492 Before: func() { 493 hostHandle, err := s.State.Machine(host.Id()) 494 c.Assert(err, jc.ErrorIsNil) 495 496 // Original assertion preventing host removal is no longer valid 497 c.Assert(hostHandle.SetHasVote(false), gc.IsNil) 498 499 // But now the host gets a colocated unit, a different condition preventing removal 500 c.Assert(colocated.AssignToMachine(hostHandle), gc.IsNil) 501 }, 502 }).Check() 503 504 c.Assert(target.Destroy(), gc.IsNil) 505 assertLife(c, host, state.Alive) 506 } 507 508 func (s *UnitSuite) TestRefresh(c *gc.C) { 509 unit1, err := s.State.Unit(s.unit.Name()) 510 c.Assert(err, jc.ErrorIsNil) 511 512 err = s.unit.SetPassword("arble-farble-dying-yarble") 513 c.Assert(err, jc.ErrorIsNil) 514 valid := unit1.PasswordValid("arble-farble-dying-yarble") 515 c.Assert(valid, jc.IsFalse) 516 err = unit1.Refresh() 517 c.Assert(err, jc.ErrorIsNil) 518 valid = unit1.PasswordValid("arble-farble-dying-yarble") 519 c.Assert(valid, jc.IsTrue) 520 521 err = unit1.EnsureDead() 522 c.Assert(err, jc.ErrorIsNil) 523 err = unit1.Remove() 524 c.Assert(err, jc.ErrorIsNil) 525 err = unit1.Refresh() 526 c.Assert(err, jc.Satisfies, errors.IsNotFound) 527 } 528 529 func (s *UnitSuite) TestGetSetStatusWhileAlive(c *gc.C) { 530 err := s.unit.SetStatus(state.StatusError, "", nil) 531 c.Assert(err, gc.ErrorMatches, `cannot set status "error" without info`) 532 err = s.unit.SetStatus(state.StatusAllocating, "", nil) 533 c.Assert(err, gc.ErrorMatches, `cannot set status "allocating"`) 534 err = s.unit.SetStatus(state.StatusFailed, "", nil) 535 c.Assert(err, gc.ErrorMatches, `cannot set status "failed"`) 536 err = s.unit.SetStatus(state.Status("vliegkat"), "orville", nil) 537 c.Assert(err, gc.ErrorMatches, `cannot set invalid status "vliegkat"`) 538 539 status, info, data, err := s.unit.Status() 540 c.Assert(err, jc.ErrorIsNil) 541 c.Assert(status, gc.Equals, state.StatusAllocating) 542 c.Assert(info, gc.Equals, "") 543 c.Assert(data, gc.HasLen, 0) 544 545 err = s.unit.SetStatus(state.StatusActive, "", nil) 546 c.Assert(err, jc.ErrorIsNil) 547 status, info, data, err = s.unit.Status() 548 c.Assert(err, jc.ErrorIsNil) 549 c.Assert(status, gc.Equals, state.StatusActive) 550 c.Assert(info, gc.Equals, "") 551 c.Assert(data, gc.HasLen, 0) 552 553 err = s.unit.SetStatus(state.StatusError, "test-hook failed", map[string]interface{}{ 554 "foo": "bar", 555 }) 556 c.Assert(err, jc.ErrorIsNil) 557 status, info, data, err = s.unit.Status() 558 c.Assert(err, jc.ErrorIsNil) 559 c.Assert(status, gc.Equals, state.StatusError) 560 c.Assert(info, gc.Equals, "test-hook failed") 561 c.Assert(data, gc.DeepEquals, map[string]interface{}{ 562 "foo": "bar", 563 }) 564 } 565 566 func (s *UnitSuite) TestGetSetStatusWhileNotAlive(c *gc.C) { 567 err := s.unit.Destroy() 568 c.Assert(err, jc.ErrorIsNil) 569 err = s.unit.SetStatus(state.StatusActive, "not really", nil) 570 c.Assert(err, gc.ErrorMatches, `cannot set status of unit "wordpress/0": not found or dead`) 571 _, _, _, err = s.unit.Status() 572 c.Assert(err, gc.ErrorMatches, "status not found") 573 574 err = s.unit.EnsureDead() 575 c.Assert(err, jc.ErrorIsNil) 576 err = s.unit.SetStatus(state.StatusActive, "not really", nil) 577 c.Assert(err, gc.ErrorMatches, `cannot set status of unit "wordpress/0": not found or dead`) 578 _, _, _, err = s.unit.Status() 579 c.Assert(err, gc.ErrorMatches, "status not found") 580 } 581 582 func (s *UnitSuite) TestGetSetStatusDataStandard(c *gc.C) { 583 err := s.unit.SetStatus(state.StatusActive, "", nil) 584 c.Assert(err, jc.ErrorIsNil) 585 _, _, _, err = s.unit.Status() 586 c.Assert(err, jc.ErrorIsNil) 587 588 // Regular status setting with data. 589 err = s.unit.SetStatus(state.StatusError, "test-hook failed", map[string]interface{}{ 590 "1st-key": "one", 591 "2nd-key": 2, 592 "3rd-key": true, 593 }) 594 c.Assert(err, jc.ErrorIsNil) 595 596 status, info, data, err := s.unit.Status() 597 c.Assert(err, jc.ErrorIsNil) 598 c.Assert(status, gc.Equals, state.StatusError) 599 c.Assert(info, gc.Equals, "test-hook failed") 600 c.Assert(data, gc.DeepEquals, map[string]interface{}{ 601 "1st-key": "one", 602 "2nd-key": 2, 603 "3rd-key": true, 604 }) 605 } 606 607 func (s *UnitSuite) TestGetSetStatusDataMongo(c *gc.C) { 608 err := s.unit.SetStatus(state.StatusActive, "", nil) 609 c.Assert(err, jc.ErrorIsNil) 610 _, _, _, err = s.unit.Status() 611 c.Assert(err, jc.ErrorIsNil) 612 613 // Status setting with MongoDB special values. 614 err = s.unit.SetStatus(state.StatusError, "mongo", map[string]interface{}{ 615 `{name: "Joe"}`: "$where", 616 "eval": `eval(function(foo) { return foo; }, "bar")`, 617 "mapReduce": "mapReduce", 618 "group": "group", 619 }) 620 c.Assert(err, jc.ErrorIsNil) 621 622 status, info, data, err := s.unit.Status() 623 c.Assert(err, jc.ErrorIsNil) 624 c.Assert(status, gc.Equals, state.StatusError) 625 c.Assert(info, gc.Equals, "mongo") 626 c.Assert(data, gc.DeepEquals, map[string]interface{}{ 627 `{name: "Joe"}`: "$where", 628 "eval": `eval(function(foo) { return foo; }, "bar")`, 629 "mapReduce": "mapReduce", 630 "group": "group", 631 }) 632 } 633 634 func (s *UnitSuite) TestGetSetStatusDataChange(c *gc.C) { 635 err := s.unit.SetStatus(state.StatusActive, "", nil) 636 c.Assert(err, jc.ErrorIsNil) 637 _, _, _, err = s.unit.Status() 638 c.Assert(err, jc.ErrorIsNil) 639 640 // Status setting and changing data afterwards. 641 data := map[string]interface{}{ 642 "1st-key": "one", 643 "2nd-key": 2, 644 "3rd-key": true, 645 } 646 err = s.unit.SetStatus(state.StatusError, "test-hook failed", data) 647 c.Assert(err, jc.ErrorIsNil) 648 data["4th-key"] = 4.0 649 650 status, info, data, err := s.unit.Status() 651 c.Assert(err, jc.ErrorIsNil) 652 c.Assert(status, gc.Equals, state.StatusError) 653 c.Assert(info, gc.Equals, "test-hook failed") 654 c.Assert(data, gc.DeepEquals, map[string]interface{}{ 655 "1st-key": "one", 656 "2nd-key": 2, 657 "3rd-key": true, 658 }) 659 660 // Set status data to nil, so an empty map will be returned. 661 err = s.unit.SetStatus(state.StatusActive, "", nil) 662 c.Assert(err, jc.ErrorIsNil) 663 664 status, info, data, err = s.unit.Status() 665 c.Assert(err, jc.ErrorIsNil) 666 c.Assert(status, gc.Equals, state.StatusActive) 667 c.Assert(info, gc.Equals, "") 668 c.Assert(data, gc.HasLen, 0) 669 } 670 671 func (s *UnitSuite) TestSetCharmURLSuccess(c *gc.C) { 672 preventUnitDestroyRemove(c, s.unit) 673 curl, ok := s.unit.CharmURL() 674 c.Assert(ok, jc.IsFalse) 675 c.Assert(curl, gc.IsNil) 676 677 err := s.unit.SetCharmURL(s.charm.URL()) 678 c.Assert(err, jc.ErrorIsNil) 679 680 curl, ok = s.unit.CharmURL() 681 c.Assert(ok, jc.IsTrue) 682 c.Assert(curl, gc.DeepEquals, s.charm.URL()) 683 } 684 685 func (s *UnitSuite) TestSetCharmURLFailures(c *gc.C) { 686 preventUnitDestroyRemove(c, s.unit) 687 curl, ok := s.unit.CharmURL() 688 c.Assert(ok, jc.IsFalse) 689 c.Assert(curl, gc.IsNil) 690 691 err := s.unit.SetCharmURL(nil) 692 c.Assert(err, gc.ErrorMatches, "cannot set nil charm url") 693 694 err = s.unit.SetCharmURL(charm.MustParseURL("cs:missing/one-1")) 695 c.Assert(err, gc.ErrorMatches, `unknown charm url "cs:missing/one-1"`) 696 697 err = s.unit.EnsureDead() 698 c.Assert(err, jc.ErrorIsNil) 699 err = s.unit.SetCharmURL(s.charm.URL()) 700 c.Assert(err, gc.Equals, state.ErrDead) 701 } 702 703 func (s *UnitSuite) TestSetCharmURLWithRemovedUnit(c *gc.C) { 704 err := s.unit.Destroy() 705 c.Assert(err, jc.ErrorIsNil) 706 assertRemoved(c, s.unit) 707 708 err = s.unit.SetCharmURL(s.charm.URL()) 709 c.Assert(err, gc.Equals, state.ErrDead) 710 } 711 712 func (s *UnitSuite) TestSetCharmURLWithDyingUnit(c *gc.C) { 713 preventUnitDestroyRemove(c, s.unit) 714 err := s.unit.Destroy() 715 c.Assert(err, jc.ErrorIsNil) 716 assertLife(c, s.unit, state.Dying) 717 718 err = s.unit.SetCharmURL(s.charm.URL()) 719 c.Assert(err, jc.ErrorIsNil) 720 721 curl, ok := s.unit.CharmURL() 722 c.Assert(ok, jc.IsTrue) 723 c.Assert(curl, gc.DeepEquals, s.charm.URL()) 724 } 725 726 func (s *UnitSuite) TestSetCharmURLRetriesWithDeadUnit(c *gc.C) { 727 preventUnitDestroyRemove(c, s.unit) 728 729 defer state.SetBeforeHooks(c, s.State, func() { 730 err := s.unit.Destroy() 731 c.Assert(err, jc.ErrorIsNil) 732 err = s.unit.EnsureDead() 733 c.Assert(err, jc.ErrorIsNil) 734 assertLife(c, s.unit, state.Dead) 735 }).Check() 736 737 err := s.unit.SetCharmURL(s.charm.URL()) 738 c.Assert(err, gc.Equals, state.ErrDead) 739 } 740 741 func (s *UnitSuite) TestSetCharmURLRetriesWithDifferentURL(c *gc.C) { 742 sch := s.AddConfigCharm(c, "wordpress", emptyConfig, 2) 743 744 defer state.SetTestHooks(c, s.State, 745 jujutxn.TestHook{ 746 Before: func() { 747 // Set a different charm to force a retry: first on 748 // the service, so the settings are created, then on 749 // the unit. 750 err := s.service.SetCharm(sch, false) 751 c.Assert(err, jc.ErrorIsNil) 752 err = s.unit.SetCharmURL(sch.URL()) 753 c.Assert(err, jc.ErrorIsNil) 754 }, 755 After: func() { 756 // Set back the same charm on the service, so the 757 // settings refcount is correct.. 758 err := s.service.SetCharm(s.charm, false) 759 c.Assert(err, jc.ErrorIsNil) 760 }, 761 }, 762 jujutxn.TestHook{ 763 Before: nil, // Ensure there will be a retry. 764 After: func() { 765 // Verify it worked after the second attempt. 766 err := s.unit.Refresh() 767 c.Assert(err, jc.ErrorIsNil) 768 currentURL, hasURL := s.unit.CharmURL() 769 c.Assert(currentURL, jc.DeepEquals, s.charm.URL()) 770 c.Assert(hasURL, jc.IsTrue) 771 }, 772 }, 773 ).Check() 774 775 err := s.unit.SetCharmURL(s.charm.URL()) 776 c.Assert(err, jc.ErrorIsNil) 777 } 778 779 func (s *UnitSuite) TestDestroySetStatusRetry(c *gc.C) { 780 defer state.SetRetryHooks(c, s.State, func() { 781 err := s.unit.SetStatus(state.StatusActive, "", nil) 782 c.Assert(err, jc.ErrorIsNil) 783 }, func() { 784 assertLife(c, s.unit, state.Dying) 785 }).Check() 786 787 err := s.unit.Destroy() 788 c.Assert(err, jc.ErrorIsNil) 789 } 790 791 func (s *UnitSuite) TestDestroySetCharmRetry(c *gc.C) { 792 defer state.SetRetryHooks(c, s.State, func() { 793 err := s.unit.SetCharmURL(s.charm.URL()) 794 c.Assert(err, jc.ErrorIsNil) 795 }, func() { 796 assertRemoved(c, s.unit) 797 }).Check() 798 799 err := s.unit.Destroy() 800 c.Assert(err, jc.ErrorIsNil) 801 } 802 803 func (s *UnitSuite) TestDestroyChangeCharmRetry(c *gc.C) { 804 err := s.unit.SetCharmURL(s.charm.URL()) 805 c.Assert(err, jc.ErrorIsNil) 806 newCharm := s.AddConfigCharm(c, "mysql", "options: {}", 99) 807 err = s.service.SetCharm(newCharm, false) 808 c.Assert(err, jc.ErrorIsNil) 809 810 defer state.SetRetryHooks(c, s.State, func() { 811 err := s.unit.SetCharmURL(newCharm.URL()) 812 c.Assert(err, jc.ErrorIsNil) 813 }, func() { 814 assertRemoved(c, s.unit) 815 }).Check() 816 817 err = s.unit.Destroy() 818 c.Assert(err, jc.ErrorIsNil) 819 } 820 821 func (s *UnitSuite) TestDestroyAssignRetry(c *gc.C) { 822 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 823 c.Assert(err, jc.ErrorIsNil) 824 825 defer state.SetRetryHooks(c, s.State, func() { 826 err := s.unit.AssignToMachine(machine) 827 c.Assert(err, jc.ErrorIsNil) 828 }, func() { 829 assertRemoved(c, s.unit) 830 // Also check the unit ref was properly removed from the machine doc -- 831 // if it weren't, we wouldn't be able to make the machine Dead. 832 err := machine.EnsureDead() 833 c.Assert(err, jc.ErrorIsNil) 834 }).Check() 835 836 err = s.unit.Destroy() 837 c.Assert(err, jc.ErrorIsNil) 838 } 839 840 func (s *UnitSuite) TestDestroyUnassignRetry(c *gc.C) { 841 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 842 c.Assert(err, jc.ErrorIsNil) 843 err = s.unit.AssignToMachine(machine) 844 c.Assert(err, jc.ErrorIsNil) 845 846 defer state.SetRetryHooks(c, s.State, func() { 847 err := s.unit.UnassignFromMachine() 848 c.Assert(err, jc.ErrorIsNil) 849 }, func() { 850 assertRemoved(c, s.unit) 851 }).Check() 852 853 err = s.unit.Destroy() 854 c.Assert(err, jc.ErrorIsNil) 855 } 856 857 func (s *UnitSuite) TestShortCircuitDestroyUnit(c *gc.C) { 858 // A unit that has not set any status is removed directly. 859 err := s.unit.Destroy() 860 c.Assert(err, jc.ErrorIsNil) 861 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 862 assertRemoved(c, s.unit) 863 } 864 865 func (s *UnitSuite) TestCannotShortCircuitDestroyWithSubordinates(c *gc.C) { 866 // A unit with subordinates is just set to Dying. 867 s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 868 eps, err := s.State.InferEndpoints("logging", "wordpress") 869 c.Assert(err, jc.ErrorIsNil) 870 rel, err := s.State.AddRelation(eps...) 871 c.Assert(err, jc.ErrorIsNil) 872 ru, err := rel.Unit(s.unit) 873 c.Assert(err, jc.ErrorIsNil) 874 err = ru.EnterScope(nil) 875 c.Assert(err, jc.ErrorIsNil) 876 err = s.unit.Destroy() 877 c.Assert(err, jc.ErrorIsNil) 878 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 879 assertLife(c, s.unit, state.Dying) 880 } 881 882 func (s *UnitSuite) TestCannotShortCircuitDestroyWithStatus(c *gc.C) { 883 for i, test := range []struct { 884 status state.Status 885 info string 886 }{{ 887 state.StatusActive, "", 888 }, { 889 state.StatusError, "blah", 890 }, { 891 state.StatusStopping, "", 892 }} { 893 c.Logf("test %d: %s", i, test.status) 894 unit, err := s.service.AddUnit() 895 c.Assert(err, jc.ErrorIsNil) 896 err = unit.SetStatus(test.status, test.info, nil) 897 c.Assert(err, jc.ErrorIsNil) 898 err = unit.Destroy() 899 c.Assert(err, jc.ErrorIsNil) 900 c.Assert(unit.Life(), gc.Equals, state.Dying) 901 assertLife(c, unit, state.Dying) 902 } 903 } 904 905 func (s *UnitSuite) TestShortCircuitDestroyWithProvisionedMachine(c *gc.C) { 906 // A unit assigned to a provisioned machine is still removed directly so 907 // long as it has not set status. 908 err := s.unit.AssignToNewMachine() 909 c.Assert(err, jc.ErrorIsNil) 910 mid, err := s.unit.AssignedMachineId() 911 c.Assert(err, jc.ErrorIsNil) 912 machine, err := s.State.Machine(mid) 913 c.Assert(err, jc.ErrorIsNil) 914 err = machine.SetProvisioned("i-malive", "fake_nonce", nil) 915 c.Assert(err, jc.ErrorIsNil) 916 err = s.unit.Destroy() 917 c.Assert(err, jc.ErrorIsNil) 918 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 919 assertRemoved(c, s.unit) 920 } 921 922 func assertLife(c *gc.C, entity state.Living, life state.Life) { 923 c.Assert(entity.Refresh(), gc.IsNil) 924 c.Assert(entity.Life(), gc.Equals, life) 925 } 926 927 func assertRemoved(c *gc.C, entity state.Living) { 928 err := entity.Refresh() 929 c.Assert(err, jc.Satisfies, errors.IsNotFound) 930 err = entity.Destroy() 931 c.Assert(err, jc.ErrorIsNil) 932 if entity, ok := entity.(state.AgentLiving); ok { 933 err = entity.EnsureDead() 934 c.Assert(err, jc.ErrorIsNil) 935 err = entity.Remove() 936 c.Assert(err, jc.ErrorIsNil) 937 } 938 } 939 940 func (s *UnitSuite) TestTag(c *gc.C) { 941 c.Assert(s.unit.Tag().String(), gc.Equals, "unit-wordpress-0") 942 } 943 944 func (s *UnitSuite) TestSetPassword(c *gc.C) { 945 preventUnitDestroyRemove(c, s.unit) 946 testSetPassword(c, func() (state.Authenticator, error) { 947 return s.State.Unit(s.unit.Name()) 948 }) 949 } 950 951 func (s *UnitSuite) TestSetAgentCompatPassword(c *gc.C) { 952 e, err := s.State.Unit(s.unit.Name()) 953 c.Assert(err, jc.ErrorIsNil) 954 testSetAgentCompatPassword(c, e) 955 } 956 957 func (s *UnitSuite) TestUnitSetAgentPresence(c *gc.C) { 958 alive, err := s.unit.AgentPresence() 959 c.Assert(err, jc.ErrorIsNil) 960 c.Assert(alive, jc.IsFalse) 961 962 pinger, err := s.unit.SetAgentPresence() 963 c.Assert(err, jc.ErrorIsNil) 964 c.Assert(pinger, gc.NotNil) 965 defer pinger.Stop() 966 967 s.State.StartSync() 968 alive, err = s.unit.AgentPresence() 969 c.Assert(err, jc.ErrorIsNil) 970 c.Assert(alive, jc.IsTrue) 971 } 972 973 func (s *UnitSuite) TestUnitWaitAgentPresence(c *gc.C) { 974 alive, err := s.unit.AgentPresence() 975 c.Assert(err, jc.ErrorIsNil) 976 c.Assert(alive, jc.IsFalse) 977 978 err = s.unit.WaitAgentPresence(coretesting.ShortWait) 979 c.Assert(err, gc.ErrorMatches, `waiting for agent of unit "wordpress/0": still not alive after timeout`) 980 981 pinger, err := s.unit.SetAgentPresence() 982 c.Assert(err, jc.ErrorIsNil) 983 984 s.State.StartSync() 985 err = s.unit.WaitAgentPresence(coretesting.LongWait) 986 c.Assert(err, jc.ErrorIsNil) 987 988 alive, err = s.unit.AgentPresence() 989 c.Assert(err, jc.ErrorIsNil) 990 c.Assert(alive, jc.IsTrue) 991 992 err = pinger.Kill() 993 c.Assert(err, jc.ErrorIsNil) 994 995 s.State.StartSync() 996 997 alive, err = s.unit.AgentPresence() 998 c.Assert(err, jc.ErrorIsNil) 999 c.Assert(alive, jc.IsFalse) 1000 } 1001 1002 func (s *UnitSuite) TestResolve(c *gc.C) { 1003 err := s.unit.Resolve(false) 1004 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`) 1005 err = s.unit.Resolve(true) 1006 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`) 1007 1008 err = s.unit.SetStatus(state.StatusError, "gaaah", nil) 1009 c.Assert(err, jc.ErrorIsNil) 1010 err = s.unit.Resolve(false) 1011 c.Assert(err, jc.ErrorIsNil) 1012 err = s.unit.Resolve(true) 1013 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) 1014 c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks) 1015 1016 err = s.unit.ClearResolved() 1017 c.Assert(err, jc.ErrorIsNil) 1018 err = s.unit.Resolve(true) 1019 c.Assert(err, jc.ErrorIsNil) 1020 err = s.unit.Resolve(false) 1021 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) 1022 c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedRetryHooks) 1023 } 1024 1025 func (s *UnitSuite) TestGetSetClearResolved(c *gc.C) { 1026 mode := s.unit.Resolved() 1027 c.Assert(mode, gc.Equals, state.ResolvedNone) 1028 1029 err := s.unit.SetResolved(state.ResolvedNoHooks) 1030 c.Assert(err, jc.ErrorIsNil) 1031 err = s.unit.SetResolved(state.ResolvedNoHooks) 1032 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) 1033 1034 mode = s.unit.Resolved() 1035 c.Assert(mode, gc.Equals, state.ResolvedNoHooks) 1036 err = s.unit.Refresh() 1037 c.Assert(err, jc.ErrorIsNil) 1038 mode = s.unit.Resolved() 1039 c.Assert(mode, gc.Equals, state.ResolvedNoHooks) 1040 1041 err = s.unit.ClearResolved() 1042 c.Assert(err, jc.ErrorIsNil) 1043 mode = s.unit.Resolved() 1044 c.Assert(mode, gc.Equals, state.ResolvedNone) 1045 err = s.unit.Refresh() 1046 c.Assert(err, jc.ErrorIsNil) 1047 mode = s.unit.Resolved() 1048 c.Assert(mode, gc.Equals, state.ResolvedNone) 1049 err = s.unit.ClearResolved() 1050 c.Assert(err, jc.ErrorIsNil) 1051 1052 err = s.unit.SetResolved(state.ResolvedNone) 1053 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: ""`) 1054 err = s.unit.SetResolved(state.ResolvedMode("foo")) 1055 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: "foo"`) 1056 } 1057 1058 func (s *UnitSuite) TestOpenedPorts(c *gc.C) { 1059 // Verify ports can be opened and closed only when the unit has 1060 // assigned machine. 1061 err := s.unit.OpenPort("tcp", 10) 1062 c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned) 1063 err = s.unit.OpenPorts("tcp", 10, 20) 1064 c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned) 1065 err = s.unit.ClosePort("tcp", 10) 1066 c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned) 1067 err = s.unit.ClosePorts("tcp", 10, 20) 1068 c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned) 1069 open, err := s.unit.OpenedPorts() 1070 c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned) 1071 c.Assert(open, gc.HasLen, 0) 1072 1073 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1074 c.Assert(err, jc.ErrorIsNil) 1075 err = s.unit.AssignToMachine(machine) 1076 c.Assert(err, jc.ErrorIsNil) 1077 1078 // Verify no open ports before activity. 1079 open, err = s.unit.OpenedPorts() 1080 c.Assert(err, jc.ErrorIsNil) 1081 c.Assert(open, gc.HasLen, 0) 1082 1083 // Now open and close ports and ranges and check. 1084 1085 err = s.unit.OpenPort("tcp", 80) 1086 c.Assert(err, jc.ErrorIsNil) 1087 err = s.unit.OpenPorts("udp", 100, 200) 1088 c.Assert(err, jc.ErrorIsNil) 1089 open, err = s.unit.OpenedPorts() 1090 c.Assert(err, jc.ErrorIsNil) 1091 c.Assert(open, gc.DeepEquals, []network.PortRange{ 1092 {80, 80, "tcp"}, 1093 {100, 200, "udp"}, 1094 }) 1095 1096 err = s.unit.OpenPort("udp", 53) 1097 c.Assert(err, jc.ErrorIsNil) 1098 open, err = s.unit.OpenedPorts() 1099 c.Assert(err, jc.ErrorIsNil) 1100 c.Assert(open, gc.DeepEquals, []network.PortRange{ 1101 {80, 80, "tcp"}, 1102 {53, 53, "udp"}, 1103 {100, 200, "udp"}, 1104 }) 1105 1106 err = s.unit.OpenPorts("tcp", 53, 55) 1107 c.Assert(err, jc.ErrorIsNil) 1108 open, err = s.unit.OpenedPorts() 1109 c.Assert(err, jc.ErrorIsNil) 1110 c.Assert(open, gc.DeepEquals, []network.PortRange{ 1111 {53, 55, "tcp"}, 1112 {80, 80, "tcp"}, 1113 {53, 53, "udp"}, 1114 {100, 200, "udp"}, 1115 }) 1116 1117 err = s.unit.OpenPort("tcp", 443) 1118 c.Assert(err, jc.ErrorIsNil) 1119 open, err = s.unit.OpenedPorts() 1120 c.Assert(err, jc.ErrorIsNil) 1121 c.Assert(open, gc.DeepEquals, []network.PortRange{ 1122 {53, 55, "tcp"}, 1123 {80, 80, "tcp"}, 1124 {443, 443, "tcp"}, 1125 {53, 53, "udp"}, 1126 {100, 200, "udp"}, 1127 }) 1128 1129 err = s.unit.ClosePort("tcp", 80) 1130 c.Assert(err, jc.ErrorIsNil) 1131 open, err = s.unit.OpenedPorts() 1132 c.Assert(err, jc.ErrorIsNil) 1133 c.Assert(open, gc.DeepEquals, []network.PortRange{ 1134 {53, 55, "tcp"}, 1135 {443, 443, "tcp"}, 1136 {53, 53, "udp"}, 1137 {100, 200, "udp"}, 1138 }) 1139 1140 err = s.unit.ClosePorts("udp", 100, 200) 1141 c.Assert(err, jc.ErrorIsNil) 1142 open, err = s.unit.OpenedPorts() 1143 c.Assert(err, jc.ErrorIsNil) 1144 c.Assert(open, gc.DeepEquals, []network.PortRange{ 1145 {53, 55, "tcp"}, 1146 {443, 443, "tcp"}, 1147 {53, 53, "udp"}, 1148 }) 1149 } 1150 1151 func (s *UnitSuite) TestOpenClosePortWhenDying(c *gc.C) { 1152 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1153 c.Assert(err, jc.ErrorIsNil) 1154 err = s.unit.AssignToMachine(machine) 1155 c.Assert(err, jc.ErrorIsNil) 1156 1157 preventUnitDestroyRemove(c, s.unit) 1158 testWhenDying(c, s.unit, noErr, contentionErr, func() error { 1159 err := s.unit.OpenPort("tcp", 20) 1160 if err != nil { 1161 return err 1162 } 1163 err = s.unit.OpenPorts("tcp", 10, 15) 1164 if err != nil { 1165 return err 1166 } 1167 err = s.unit.Refresh() 1168 if err != nil { 1169 return err 1170 } 1171 err = s.unit.ClosePort("tcp", 20) 1172 if err != nil { 1173 return err 1174 } 1175 return s.unit.ClosePorts("tcp", 10, 15) 1176 }) 1177 } 1178 1179 func (s *UnitSuite) TestRemoveLastUnitOnMachineRemovesAllPorts(c *gc.C) { 1180 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1181 c.Assert(err, jc.ErrorIsNil) 1182 err = s.unit.AssignToMachine(machine) 1183 c.Assert(err, jc.ErrorIsNil) 1184 1185 ports, err := machine.AllPorts() 1186 c.Assert(err, jc.ErrorIsNil) 1187 c.Assert(ports, gc.HasLen, 0) 1188 1189 err = s.unit.OpenPorts("tcp", 100, 200) 1190 c.Assert(err, jc.ErrorIsNil) 1191 1192 ports, err = machine.AllPorts() 1193 c.Assert(err, jc.ErrorIsNil) 1194 c.Assert(ports, gc.HasLen, 1) 1195 c.Assert(ports[0].PortsForUnit(s.unit.Name()), jc.DeepEquals, []state.PortRange{ 1196 {s.unit.Name(), 100, 200, "tcp"}, 1197 }) 1198 1199 // Now remove the unit and check again. 1200 err = s.unit.EnsureDead() 1201 c.Assert(err, jc.ErrorIsNil) 1202 err = s.unit.Remove() 1203 c.Assert(err, jc.ErrorIsNil) 1204 err = s.unit.Refresh() 1205 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1206 1207 // Because that was the only range open, the ports doc will be 1208 // removed as well. 1209 ports, err = machine.AllPorts() 1210 c.Assert(err, jc.ErrorIsNil) 1211 c.Assert(ports, gc.HasLen, 0) 1212 } 1213 1214 func (s *UnitSuite) TestRemoveUnitRemovesItsPortsOnly(c *gc.C) { 1215 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1216 c.Assert(err, jc.ErrorIsNil) 1217 err = s.unit.AssignToMachine(machine) 1218 c.Assert(err, jc.ErrorIsNil) 1219 1220 otherUnit, err := s.service.AddUnit() 1221 c.Assert(err, jc.ErrorIsNil) 1222 err = otherUnit.AssignToMachine(machine) 1223 c.Assert(err, jc.ErrorIsNil) 1224 1225 ports, err := machine.AllPorts() 1226 c.Assert(err, jc.ErrorIsNil) 1227 c.Assert(ports, gc.HasLen, 0) 1228 1229 err = s.unit.OpenPorts("tcp", 100, 200) 1230 c.Assert(err, jc.ErrorIsNil) 1231 err = otherUnit.OpenPorts("udp", 300, 400) 1232 c.Assert(err, jc.ErrorIsNil) 1233 1234 ports, err = machine.AllPorts() 1235 c.Assert(err, jc.ErrorIsNil) 1236 c.Assert(ports, gc.HasLen, 1) 1237 c.Assert(ports[0].PortsForUnit(s.unit.Name()), jc.DeepEquals, []state.PortRange{ 1238 {s.unit.Name(), 100, 200, "tcp"}, 1239 }) 1240 c.Assert(ports[0].PortsForUnit(otherUnit.Name()), jc.DeepEquals, []state.PortRange{ 1241 {otherUnit.Name(), 300, 400, "udp"}, 1242 }) 1243 1244 // Now remove the first unit and check again. 1245 err = s.unit.EnsureDead() 1246 c.Assert(err, jc.ErrorIsNil) 1247 err = s.unit.Remove() 1248 c.Assert(err, jc.ErrorIsNil) 1249 err = s.unit.Refresh() 1250 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1251 1252 // Verify only otherUnit still has open ports. 1253 ports, err = machine.AllPorts() 1254 c.Assert(err, jc.ErrorIsNil) 1255 c.Assert(ports, gc.HasLen, 1) 1256 c.Assert(ports[0].PortsForUnit(s.unit.Name()), gc.HasLen, 0) 1257 c.Assert(ports[0].PortsForUnit(otherUnit.Name()), jc.DeepEquals, []state.PortRange{ 1258 {otherUnit.Name(), 300, 400, "udp"}, 1259 }) 1260 } 1261 1262 func (s *UnitSuite) TestSetClearResolvedWhenNotAlive(c *gc.C) { 1263 preventUnitDestroyRemove(c, s.unit) 1264 err := s.unit.Destroy() 1265 c.Assert(err, jc.ErrorIsNil) 1266 err = s.unit.SetResolved(state.ResolvedNoHooks) 1267 c.Assert(err, jc.ErrorIsNil) 1268 err = s.unit.Refresh() 1269 c.Assert(err, jc.ErrorIsNil) 1270 c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks) 1271 err = s.unit.ClearResolved() 1272 c.Assert(err, jc.ErrorIsNil) 1273 1274 err = s.unit.EnsureDead() 1275 c.Assert(err, jc.ErrorIsNil) 1276 err = s.unit.SetResolved(state.ResolvedRetryHooks) 1277 c.Assert(err, gc.ErrorMatches, deadErr) 1278 err = s.unit.ClearResolved() 1279 c.Assert(err, jc.ErrorIsNil) 1280 } 1281 1282 func (s *UnitSuite) TestSubordinateChangeInPrincipal(c *gc.C) { 1283 subCharm := s.AddTestingCharm(c, "logging") 1284 for i := 0; i < 2; i++ { 1285 // Note: subordinate units can only be created as a side effect of a 1286 // principal entering scope; and a given principal can only have a 1287 // single subordinate unit of each service. 1288 name := "logging" + strconv.Itoa(i) 1289 s.AddTestingService(c, name, subCharm) 1290 eps, err := s.State.InferEndpoints(name, "wordpress") 1291 c.Assert(err, jc.ErrorIsNil) 1292 rel, err := s.State.AddRelation(eps...) 1293 c.Assert(err, jc.ErrorIsNil) 1294 ru, err := rel.Unit(s.unit) 1295 c.Assert(err, jc.ErrorIsNil) 1296 err = ru.EnterScope(nil) 1297 c.Assert(err, jc.ErrorIsNil) 1298 } 1299 1300 err := s.unit.Refresh() 1301 c.Assert(err, jc.ErrorIsNil) 1302 subordinates := s.unit.SubordinateNames() 1303 c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0", "logging1/0"}) 1304 1305 su1, err := s.State.Unit("logging1/0") 1306 c.Assert(err, jc.ErrorIsNil) 1307 err = su1.EnsureDead() 1308 c.Assert(err, jc.ErrorIsNil) 1309 err = su1.Remove() 1310 c.Assert(err, jc.ErrorIsNil) 1311 err = s.unit.Refresh() 1312 c.Assert(err, jc.ErrorIsNil) 1313 subordinates = s.unit.SubordinateNames() 1314 c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0"}) 1315 } 1316 1317 func (s *UnitSuite) TestDeathWithSubordinates(c *gc.C) { 1318 // Check that units can become dead when they've never had subordinates. 1319 u, err := s.service.AddUnit() 1320 c.Assert(err, jc.ErrorIsNil) 1321 err = u.EnsureDead() 1322 c.Assert(err, jc.ErrorIsNil) 1323 1324 // Create a new unit and add a subordinate. 1325 u, err = s.service.AddUnit() 1326 c.Assert(err, jc.ErrorIsNil) 1327 s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 1328 c.Assert(err, jc.ErrorIsNil) 1329 eps, err := s.State.InferEndpoints("logging", "wordpress") 1330 c.Assert(err, jc.ErrorIsNil) 1331 rel, err := s.State.AddRelation(eps...) 1332 c.Assert(err, jc.ErrorIsNil) 1333 ru, err := rel.Unit(u) 1334 c.Assert(err, jc.ErrorIsNil) 1335 err = ru.EnterScope(nil) 1336 c.Assert(err, jc.ErrorIsNil) 1337 1338 // Check the unit cannot become Dead, but can become Dying... 1339 err = u.EnsureDead() 1340 c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates) 1341 err = u.Destroy() 1342 c.Assert(err, jc.ErrorIsNil) 1343 1344 // ...and that it still can't become Dead now it's Dying. 1345 err = u.EnsureDead() 1346 c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates) 1347 1348 // Make the subordinate Dead and check the principal still cannot be removed. 1349 sub, err := s.State.Unit("logging/0") 1350 c.Assert(err, jc.ErrorIsNil) 1351 err = sub.EnsureDead() 1352 c.Assert(err, jc.ErrorIsNil) 1353 err = u.EnsureDead() 1354 c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates) 1355 1356 // remove the subordinate and check the principal can finally become Dead. 1357 err = sub.Remove() 1358 c.Assert(err, jc.ErrorIsNil) 1359 err = u.EnsureDead() 1360 c.Assert(err, jc.ErrorIsNil) 1361 } 1362 1363 func (s *UnitSuite) TestPrincipalName(c *gc.C) { 1364 subCharm := s.AddTestingCharm(c, "logging") 1365 s.AddTestingService(c, "logging", subCharm) 1366 eps, err := s.State.InferEndpoints("logging", "wordpress") 1367 c.Assert(err, jc.ErrorIsNil) 1368 rel, err := s.State.AddRelation(eps...) 1369 c.Assert(err, jc.ErrorIsNil) 1370 ru, err := rel.Unit(s.unit) 1371 c.Assert(err, jc.ErrorIsNil) 1372 err = ru.EnterScope(nil) 1373 c.Assert(err, jc.ErrorIsNil) 1374 1375 err = s.unit.Refresh() 1376 c.Assert(err, jc.ErrorIsNil) 1377 subordinates := s.unit.SubordinateNames() 1378 c.Assert(subordinates, gc.DeepEquals, []string{"logging/0"}) 1379 1380 su, err := s.State.Unit("logging/0") 1381 c.Assert(err, jc.ErrorIsNil) 1382 principal, valid := su.PrincipalName() 1383 c.Assert(valid, jc.IsTrue) 1384 c.Assert(principal, gc.Equals, s.unit.Name()) 1385 1386 // Calling PrincipalName on a principal unit yields "", false. 1387 principal, valid = s.unit.PrincipalName() 1388 c.Assert(valid, jc.IsFalse) 1389 c.Assert(principal, gc.Equals, "") 1390 } 1391 1392 func (s *UnitSuite) TestRelations(c *gc.C) { 1393 wordpress0 := s.unit 1394 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 1395 mysql0, err := mysql.AddUnit() 1396 c.Assert(err, jc.ErrorIsNil) 1397 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1398 c.Assert(err, jc.ErrorIsNil) 1399 rel, err := s.State.AddRelation(eps...) 1400 c.Assert(err, jc.ErrorIsNil) 1401 1402 assertEquals := func(actual, expect []*state.Relation) { 1403 c.Assert(actual, gc.HasLen, len(expect)) 1404 for i, a := range actual { 1405 c.Assert(a.Id(), gc.Equals, expect[i].Id()) 1406 } 1407 } 1408 assertRelationsJoined := func(unit *state.Unit, expect ...*state.Relation) { 1409 actual, err := unit.RelationsJoined() 1410 c.Assert(err, jc.ErrorIsNil) 1411 assertEquals(actual, expect) 1412 } 1413 assertRelationsInScope := func(unit *state.Unit, expect ...*state.Relation) { 1414 actual, err := unit.RelationsInScope() 1415 c.Assert(err, jc.ErrorIsNil) 1416 assertEquals(actual, expect) 1417 } 1418 assertRelations := func(unit *state.Unit, expect ...*state.Relation) { 1419 assertRelationsInScope(unit, expect...) 1420 assertRelationsJoined(unit, expect...) 1421 } 1422 assertRelations(wordpress0) 1423 assertRelations(mysql0) 1424 1425 mysql0ru, err := rel.Unit(mysql0) 1426 c.Assert(err, jc.ErrorIsNil) 1427 err = mysql0ru.EnterScope(nil) 1428 c.Assert(err, jc.ErrorIsNil) 1429 assertRelations(wordpress0) 1430 assertRelations(mysql0, rel) 1431 1432 wordpress0ru, err := rel.Unit(wordpress0) 1433 c.Assert(err, jc.ErrorIsNil) 1434 err = wordpress0ru.EnterScope(nil) 1435 c.Assert(err, jc.ErrorIsNil) 1436 assertRelations(wordpress0, rel) 1437 assertRelations(mysql0, rel) 1438 1439 err = mysql0ru.PrepareLeaveScope() 1440 c.Assert(err, jc.ErrorIsNil) 1441 assertRelations(wordpress0, rel) 1442 assertRelationsInScope(mysql0, rel) 1443 assertRelationsJoined(mysql0) 1444 } 1445 1446 func (s *UnitSuite) TestRemove(c *gc.C) { 1447 err := s.unit.Remove() 1448 c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`) 1449 err = s.unit.EnsureDead() 1450 c.Assert(err, jc.ErrorIsNil) 1451 err = s.unit.Remove() 1452 c.Assert(err, jc.ErrorIsNil) 1453 err = s.unit.Refresh() 1454 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1455 units, err := s.service.AllUnits() 1456 c.Assert(err, jc.ErrorIsNil) 1457 c.Assert(units, gc.HasLen, 0) 1458 err = s.unit.Remove() 1459 c.Assert(err, jc.ErrorIsNil) 1460 } 1461 1462 func (s *UnitSuite) TestRemovePathological(c *gc.C) { 1463 // Add a relation between wordpress and mysql... 1464 wordpress := s.service 1465 wordpress0 := s.unit 1466 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 1467 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1468 c.Assert(err, jc.ErrorIsNil) 1469 rel, err := s.State.AddRelation(eps...) 1470 c.Assert(err, jc.ErrorIsNil) 1471 1472 // The relation holds a reference to wordpress, but that can't keep 1473 // wordpress from being removed -- because the relation will be removed 1474 // if we destroy wordpress. 1475 // However, if a unit of the *other* service joins the relation, that 1476 // will add an additional reference and prevent the relation -- and 1477 // thus wordpress itself -- from being removed when its last unit is. 1478 mysql0, err := mysql.AddUnit() 1479 c.Assert(err, jc.ErrorIsNil) 1480 mysql0ru, err := rel.Unit(mysql0) 1481 c.Assert(err, jc.ErrorIsNil) 1482 err = mysql0ru.EnterScope(nil) 1483 c.Assert(err, jc.ErrorIsNil) 1484 1485 // Destroy wordpress, and remove its last unit. 1486 err = wordpress.Destroy() 1487 c.Assert(err, jc.ErrorIsNil) 1488 err = wordpress0.EnsureDead() 1489 c.Assert(err, jc.ErrorIsNil) 1490 err = wordpress0.Remove() 1491 c.Assert(err, jc.ErrorIsNil) 1492 1493 // Check this didn't kill the service or relation yet... 1494 err = wordpress.Refresh() 1495 c.Assert(err, jc.ErrorIsNil) 1496 err = rel.Refresh() 1497 c.Assert(err, jc.ErrorIsNil) 1498 1499 // ...but when the unit on the other side departs the relation, the 1500 // relation and the other service are cleaned up. 1501 err = mysql0ru.LeaveScope() 1502 c.Assert(err, jc.ErrorIsNil) 1503 err = wordpress.Refresh() 1504 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1505 err = rel.Refresh() 1506 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1507 } 1508 1509 func (s *UnitSuite) TestRemovePathologicalWithBuggyUniter(c *gc.C) { 1510 // Add a relation between wordpress and mysql... 1511 wordpress := s.service 1512 wordpress0 := s.unit 1513 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 1514 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1515 c.Assert(err, jc.ErrorIsNil) 1516 rel, err := s.State.AddRelation(eps...) 1517 c.Assert(err, jc.ErrorIsNil) 1518 1519 // The relation holds a reference to wordpress, but that can't keep 1520 // wordpress from being removed -- because the relation will be removed 1521 // if we destroy wordpress. 1522 // However, if a unit of the *other* service joins the relation, that 1523 // will add an additional reference and prevent the relation -- and 1524 // thus wordpress itself -- from being removed when its last unit is. 1525 mysql0, err := mysql.AddUnit() 1526 c.Assert(err, jc.ErrorIsNil) 1527 mysql0ru, err := rel.Unit(mysql0) 1528 c.Assert(err, jc.ErrorIsNil) 1529 err = mysql0ru.EnterScope(nil) 1530 c.Assert(err, jc.ErrorIsNil) 1531 1532 // Destroy wordpress, and remove its last unit. 1533 err = wordpress.Destroy() 1534 c.Assert(err, jc.ErrorIsNil) 1535 err = wordpress0.EnsureDead() 1536 c.Assert(err, jc.ErrorIsNil) 1537 err = wordpress0.Remove() 1538 c.Assert(err, jc.ErrorIsNil) 1539 1540 // Check this didn't kill the service or relation yet... 1541 err = wordpress.Refresh() 1542 c.Assert(err, jc.ErrorIsNil) 1543 err = rel.Refresh() 1544 c.Assert(err, jc.ErrorIsNil) 1545 1546 // ...and that when the malfunctioning unit agent on the other side 1547 // sets itself to dead *without* departing the relation, the unit's 1548 // removal causes the relation and the other service to be cleaned up. 1549 err = mysql0.EnsureDead() 1550 c.Assert(err, jc.ErrorIsNil) 1551 err = mysql0.Remove() 1552 c.Assert(err, jc.ErrorIsNil) 1553 err = wordpress.Refresh() 1554 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1555 err = rel.Refresh() 1556 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1557 } 1558 1559 func (s *UnitSuite) TestWatchSubordinates(c *gc.C) { 1560 // TODO(mjs) - ENVUUID - test with multiple environments with 1561 // identically named units and ensure there's no leakage. 1562 w := s.unit.WatchSubordinateUnits() 1563 defer testing.AssertStop(c, w) 1564 wc := testing.NewStringsWatcherC(c, s.State, w) 1565 wc.AssertChange() 1566 wc.AssertNoChange() 1567 1568 // Add a couple of subordinates, check change. 1569 subCharm := s.AddTestingCharm(c, "logging") 1570 var subUnits []*state.Unit 1571 for i := 0; i < 2; i++ { 1572 // Note: subordinate units can only be created as a side effect of a 1573 // principal entering scope; and a given principal can only have a 1574 // single subordinate unit of each service. 1575 name := "logging" + strconv.Itoa(i) 1576 subSvc := s.AddTestingService(c, name, subCharm) 1577 eps, err := s.State.InferEndpoints(name, "wordpress") 1578 c.Assert(err, jc.ErrorIsNil) 1579 rel, err := s.State.AddRelation(eps...) 1580 c.Assert(err, jc.ErrorIsNil) 1581 ru, err := rel.Unit(s.unit) 1582 c.Assert(err, jc.ErrorIsNil) 1583 err = ru.EnterScope(nil) 1584 c.Assert(err, jc.ErrorIsNil) 1585 units, err := subSvc.AllUnits() 1586 c.Assert(err, jc.ErrorIsNil) 1587 c.Assert(units, gc.HasLen, 1) 1588 subUnits = append(subUnits, units[0]) 1589 } 1590 wc.AssertChange(subUnits[0].Name(), subUnits[1].Name()) 1591 wc.AssertNoChange() 1592 1593 // Set one to Dying, check change. 1594 err := subUnits[0].Destroy() 1595 c.Assert(err, jc.ErrorIsNil) 1596 wc.AssertChange(subUnits[0].Name()) 1597 wc.AssertNoChange() 1598 1599 // Set both to Dead, and remove one; check change. 1600 err = subUnits[0].EnsureDead() 1601 c.Assert(err, jc.ErrorIsNil) 1602 err = subUnits[1].EnsureDead() 1603 c.Assert(err, jc.ErrorIsNil) 1604 err = subUnits[1].Remove() 1605 c.Assert(err, jc.ErrorIsNil) 1606 wc.AssertChange(subUnits[0].Name(), subUnits[1].Name()) 1607 wc.AssertNoChange() 1608 1609 // Stop watcher, check closed. 1610 testing.AssertStop(c, w) 1611 wc.AssertClosed() 1612 1613 // Start a new watch, check Dead unit is reported. 1614 w = s.unit.WatchSubordinateUnits() 1615 defer testing.AssertStop(c, w) 1616 wc = testing.NewStringsWatcherC(c, s.State, w) 1617 wc.AssertChange(subUnits[0].Name()) 1618 wc.AssertNoChange() 1619 1620 // Remove the leftover, check no change. 1621 err = subUnits[0].Remove() 1622 c.Assert(err, jc.ErrorIsNil) 1623 wc.AssertNoChange() 1624 } 1625 1626 func (s *UnitSuite) TestWatchUnit(c *gc.C) { 1627 preventUnitDestroyRemove(c, s.unit) 1628 w := s.unit.Watch() 1629 defer testing.AssertStop(c, w) 1630 1631 // Initial event. 1632 wc := testing.NewNotifyWatcherC(c, s.State, w) 1633 wc.AssertOneChange() 1634 1635 // Make one change (to a separate instance), check one event. 1636 unit, err := s.State.Unit(s.unit.Name()) 1637 c.Assert(err, jc.ErrorIsNil) 1638 s.setAssignedMachineAddresses(c, unit) 1639 wc.AssertOneChange() 1640 1641 // Make two changes, check one event. 1642 err = unit.SetPassword("arble-farble-dying-yarble") 1643 c.Assert(err, jc.ErrorIsNil) 1644 err = unit.Destroy() 1645 c.Assert(err, jc.ErrorIsNil) 1646 wc.AssertOneChange() 1647 1648 // Stop, check closed. 1649 testing.AssertStop(c, w) 1650 wc.AssertClosed() 1651 1652 // Remove unit, start new watch, check single event. 1653 err = unit.EnsureDead() 1654 c.Assert(err, jc.ErrorIsNil) 1655 err = unit.Remove() 1656 c.Assert(err, jc.ErrorIsNil) 1657 w = s.unit.Watch() 1658 defer testing.AssertStop(c, w) 1659 testing.NewNotifyWatcherC(c, s.State, w).AssertOneChange() 1660 } 1661 1662 func (s *UnitSuite) TestUnitAgentTools(c *gc.C) { 1663 preventUnitDestroyRemove(c, s.unit) 1664 testAgentTools(c, s.unit, `unit "wordpress/0"`) 1665 } 1666 1667 func (s *UnitSuite) TestActionSpecs(c *gc.C) { 1668 basicActions := ` 1669 snapshot: 1670 params: 1671 outfile: 1672 type: string 1673 `[1:] 1674 1675 wordpress := s.AddTestingService(c, "wordpress-actions", s.AddActionsCharm(c, "wordpress", basicActions, 1)) 1676 unit1, err := wordpress.AddUnit() 1677 c.Assert(err, jc.ErrorIsNil) 1678 specs, err := unit1.ActionSpecs() 1679 c.Assert(err, jc.ErrorIsNil) 1680 c.Check(specs, jc.DeepEquals, state.ActionSpecsByName{ 1681 "snapshot": charm.ActionSpec{ 1682 Description: "No description", 1683 Params: map[string]interface{}{ 1684 "type": "object", 1685 "title": "snapshot", 1686 "description": "No description", 1687 "properties": map[string]interface{}{ 1688 "outfile": map[string]interface{}{ 1689 "type": "string", 1690 }, 1691 }, 1692 }, 1693 }, 1694 }) 1695 } 1696 1697 func (s *UnitSuite) TestUnitActionsFindsRightActions(c *gc.C) { 1698 // An actions.yaml which permits actions by the following names 1699 basicActions := ` 1700 action-a-a: 1701 action-a-b: 1702 action-a-c: 1703 action-b-a: 1704 action-b-b: 1705 `[1:] 1706 1707 // Add simple service and two units 1708 dummy := s.AddTestingService(c, "dummy", s.AddActionsCharm(c, "dummy", basicActions, 1)) 1709 1710 unit1, err := dummy.AddUnit() 1711 c.Assert(err, jc.ErrorIsNil) 1712 1713 unit2, err := dummy.AddUnit() 1714 c.Assert(err, jc.ErrorIsNil) 1715 1716 // Add 3 actions to first unit, and 2 to the second unit 1717 _, err = unit1.AddAction("action-a-a", nil) 1718 c.Assert(err, jc.ErrorIsNil) 1719 _, err = unit1.AddAction("action-a-b", nil) 1720 c.Assert(err, jc.ErrorIsNil) 1721 _, err = unit1.AddAction("action-a-c", nil) 1722 c.Assert(err, jc.ErrorIsNil) 1723 1724 _, err = unit2.AddAction("action-b-a", nil) 1725 c.Assert(err, jc.ErrorIsNil) 1726 _, err = unit2.AddAction("action-b-b", nil) 1727 c.Assert(err, jc.ErrorIsNil) 1728 1729 // Verify that calling Actions on unit1 returns only 1730 // the three actions added to unit1 1731 actions1, err := unit1.Actions() 1732 c.Assert(err, jc.ErrorIsNil) 1733 c.Assert(len(actions1), gc.Equals, 3) 1734 for _, action := range actions1 { 1735 c.Assert(action.Name(), gc.Matches, "^action-a-.") 1736 } 1737 1738 // Verify that calling Actions on unit2 returns only 1739 // the two actions added to unit2 1740 actions2, err := unit2.Actions() 1741 c.Assert(err, jc.ErrorIsNil) 1742 c.Assert(len(actions2), gc.Equals, 2) 1743 for _, action := range actions2 { 1744 c.Assert(action.Name(), gc.Matches, "^action-b-.") 1745 } 1746 }