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