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