github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/assign_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "fmt" 8 "sort" 9 "strconv" 10 "time" 11 12 "github.com/juju/names" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/txn" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/constraints" 18 "github.com/juju/juju/instance" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/storage/poolmanager" 21 "github.com/juju/juju/storage/provider" 22 "github.com/juju/juju/storage/provider/dummy" 23 "github.com/juju/juju/storage/provider/registry" 24 ) 25 26 type AssignSuite struct { 27 ConnSuite 28 wordpress *state.Service 29 } 30 31 var _ = gc.Suite(&AssignSuite{}) 32 33 func (s *AssignSuite) SetUpTest(c *gc.C) { 34 s.ConnSuite.SetUpTest(c) 35 wordpress := s.AddTestingService( 36 c, 37 "wordpress", 38 s.AddTestingCharm(c, "wordpress"), 39 ) 40 s.wordpress = wordpress 41 } 42 43 func (s *AssignSuite) addSubordinate(c *gc.C, principal *state.Unit) *state.Unit { 44 s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 45 eps, err := s.State.InferEndpoints("logging", "wordpress") 46 c.Assert(err, jc.ErrorIsNil) 47 rel, err := s.State.AddRelation(eps...) 48 c.Assert(err, jc.ErrorIsNil) 49 ru, err := rel.Unit(principal) 50 c.Assert(err, jc.ErrorIsNil) 51 err = ru.EnterScope(nil) 52 c.Assert(err, jc.ErrorIsNil) 53 subUnit, err := s.State.Unit("logging/0") 54 c.Assert(err, jc.ErrorIsNil) 55 return subUnit 56 } 57 58 func (s *AssignSuite) TestUnassignUnitFromMachineWithoutBeingAssigned(c *gc.C) { 59 unit, err := s.wordpress.AddUnit() 60 c.Assert(err, jc.ErrorIsNil) 61 // When unassigning a machine from a unit, it is possible that 62 // the machine has not been previously assigned, or that it 63 // was assigned but the state changed beneath us. In either 64 // case, the end state is the intended state, so we simply 65 // move forward without any errors here, to avoid having to 66 // handle the extra complexity of dealing with the concurrency 67 // problems. 68 err = unit.UnassignFromMachine() 69 c.Assert(err, jc.ErrorIsNil) 70 71 // Check that the unit has no machine assigned. 72 _, err = unit.AssignedMachineId() 73 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not assigned to a machine`) 74 } 75 76 func (s *AssignSuite) TestAssignUnitToMachineAgainFails(c *gc.C) { 77 unit, err := s.wordpress.AddUnit() 78 c.Assert(err, jc.ErrorIsNil) 79 // Check that assigning an already assigned unit to 80 // a machine fails if it isn't precisely the same 81 // machine. 82 machineOne, err := s.State.AddMachine("quantal", state.JobHostUnits) 83 c.Assert(err, jc.ErrorIsNil) 84 machineTwo, err := s.State.AddMachine("quantal", state.JobHostUnits) 85 c.Assert(err, jc.ErrorIsNil) 86 87 err = unit.AssignToMachine(machineOne) 88 c.Assert(err, jc.ErrorIsNil) 89 90 // Assigning the unit to the same machine should return no error. 91 err = unit.AssignToMachine(machineOne) 92 c.Assert(err, jc.ErrorIsNil) 93 94 // Assigning the unit to a different machine should fail. 95 err = unit.AssignToMachine(machineTwo) 96 c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to machine 1: unit is already assigned to a machine`) 97 98 machineId, err := unit.AssignedMachineId() 99 c.Assert(err, jc.ErrorIsNil) 100 c.Assert(machineId, gc.Equals, "0") 101 } 102 103 func (s *AssignSuite) TestAssignedMachineIdWhenNotAlive(c *gc.C) { 104 unit, err := s.wordpress.AddUnit() 105 c.Assert(err, jc.ErrorIsNil) 106 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 107 c.Assert(err, jc.ErrorIsNil) 108 109 err = unit.AssignToMachine(machine) 110 c.Assert(err, jc.ErrorIsNil) 111 112 testWhenDying(c, unit, noErr, noErr, 113 func() error { 114 _, err = unit.AssignedMachineId() 115 return err 116 }) 117 } 118 119 func (s *AssignSuite) TestAssignedMachineIdWhenPrincipalNotAlive(c *gc.C) { 120 unit, err := s.wordpress.AddUnit() 121 c.Assert(err, jc.ErrorIsNil) 122 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 123 c.Assert(err, jc.ErrorIsNil) 124 err = unit.AssignToMachine(machine) 125 c.Assert(err, jc.ErrorIsNil) 126 127 subUnit := s.addSubordinate(c, unit) 128 err = unit.Destroy() 129 c.Assert(err, jc.ErrorIsNil) 130 mid, err := subUnit.AssignedMachineId() 131 c.Assert(err, jc.ErrorIsNil) 132 c.Assert(mid, gc.Equals, machine.Id()) 133 } 134 135 func (s *AssignSuite) TestUnassignUnitFromMachineWithChangingState(c *gc.C) { 136 unit, err := s.wordpress.AddUnit() 137 c.Assert(err, jc.ErrorIsNil) 138 // Check that unassigning while the state changes fails nicely. 139 // Remove the unit for the tests. 140 err = unit.EnsureDead() 141 c.Assert(err, jc.ErrorIsNil) 142 err = unit.Remove() 143 c.Assert(err, jc.ErrorIsNil) 144 145 err = unit.UnassignFromMachine() 146 c.Assert(err, gc.ErrorMatches, `cannot unassign unit "wordpress/0" from machine: .*`) 147 _, err = unit.AssignedMachineId() 148 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not assigned to a machine`) 149 150 err = s.wordpress.Destroy() 151 c.Assert(err, jc.ErrorIsNil) 152 err = unit.UnassignFromMachine() 153 c.Assert(err, gc.ErrorMatches, `cannot unassign unit "wordpress/0" from machine: .*`) 154 _, err = unit.AssignedMachineId() 155 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not assigned to a machine`) 156 } 157 158 func (s *AssignSuite) TestAssignSubordinatesToMachine(c *gc.C) { 159 // Check that assigning a principal unit assigns its subordinates too. 160 unit, err := s.wordpress.AddUnit() 161 c.Assert(err, jc.ErrorIsNil) 162 subUnit := s.addSubordinate(c, unit) 163 164 // None of the direct unit assign methods work on subordinates. 165 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 166 c.Assert(err, jc.ErrorIsNil) 167 err = subUnit.AssignToMachine(machine) 168 c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to machine 0: unit is a subordinate`) 169 _, err = subUnit.AssignToCleanMachine() 170 c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to clean machine: unit is a subordinate`) 171 _, err = subUnit.AssignToCleanEmptyMachine() 172 c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to clean, empty machine: unit is a subordinate`) 173 err = subUnit.AssignToNewMachine() 174 c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to new machine: unit is a subordinate`) 175 176 // Subordinates know the machine they're indirectly assigned to. 177 err = unit.AssignToMachine(machine) 178 c.Assert(err, jc.ErrorIsNil) 179 id, err := subUnit.AssignedMachineId() 180 c.Assert(err, jc.ErrorIsNil) 181 c.Check(id, gc.Equals, machine.Id()) 182 183 // Unassigning the principal unassigns the subordinates too. 184 err = unit.UnassignFromMachine() 185 c.Assert(err, jc.ErrorIsNil) 186 _, err = subUnit.AssignedMachineId() 187 c.Assert(err, gc.ErrorMatches, `unit "logging/0" is not assigned to a machine`) 188 } 189 190 func (s *AssignSuite) TestDeployerTag(c *gc.C) { 191 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 192 c.Assert(err, jc.ErrorIsNil) 193 principal, err := s.wordpress.AddUnit() 194 c.Assert(err, jc.ErrorIsNil) 195 subordinate := s.addSubordinate(c, principal) 196 197 assertDeployer := func(u *state.Unit, d state.Entity) { 198 err := u.Refresh() 199 c.Assert(err, jc.ErrorIsNil) 200 name, ok := u.DeployerTag() 201 if d == nil { 202 c.Assert(ok, jc.IsFalse) 203 } else { 204 c.Assert(ok, jc.IsTrue) 205 c.Assert(name, gc.Equals, d.Tag()) 206 } 207 } 208 assertDeployer(subordinate, principal) 209 assertDeployer(principal, nil) 210 211 err = principal.AssignToMachine(machine) 212 c.Assert(err, jc.ErrorIsNil) 213 assertDeployer(subordinate, principal) 214 assertDeployer(principal, machine) 215 216 err = principal.UnassignFromMachine() 217 c.Assert(err, jc.ErrorIsNil) 218 assertDeployer(subordinate, principal) 219 assertDeployer(principal, nil) 220 } 221 222 func (s *AssignSuite) TestDirectAssignIgnoresConstraints(c *gc.C) { 223 // Set up constraints. 224 scons := constraints.MustParse("mem=2G cpu-power=400") 225 err := s.wordpress.SetConstraints(scons) 226 c.Assert(err, jc.ErrorIsNil) 227 econs := constraints.MustParse("mem=4G cpu-cores=2") 228 err = s.State.SetModelConstraints(econs) 229 c.Assert(err, jc.ErrorIsNil) 230 231 // Machine will take model constraints on creation. 232 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 233 c.Assert(err, jc.ErrorIsNil) 234 235 // Unit will take combined service/environ constraints on creation. 236 unit, err := s.wordpress.AddUnit() 237 c.Assert(err, jc.ErrorIsNil) 238 239 // Machine keeps its original constraints on direct assignment. 240 err = unit.AssignToMachine(machine) 241 c.Assert(err, jc.ErrorIsNil) 242 mcons, err := machine.Constraints() 243 c.Assert(err, jc.ErrorIsNil) 244 c.Assert(mcons, gc.DeepEquals, econs) 245 } 246 247 func (s *AssignSuite) TestAssignBadSeries(c *gc.C) { 248 machine, err := s.State.AddMachine("burble", state.JobHostUnits) 249 c.Assert(err, jc.ErrorIsNil) 250 unit, err := s.wordpress.AddUnit() 251 c.Assert(err, jc.ErrorIsNil) 252 err = unit.AssignToMachine(machine) 253 c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to machine 0: series does not match`) 254 } 255 256 func (s *AssignSuite) TestAssignMachineWhenDying(c *gc.C) { 257 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 258 c.Assert(err, jc.ErrorIsNil) 259 260 unit, err := s.wordpress.AddUnit() 261 c.Assert(err, jc.ErrorIsNil) 262 subUnit := s.addSubordinate(c, unit) 263 assignTest := func() error { 264 err := unit.AssignToMachine(machine) 265 c.Assert(unit.UnassignFromMachine(), gc.IsNil) 266 if subUnit != nil { 267 err := subUnit.EnsureDead() 268 c.Assert(err, jc.ErrorIsNil) 269 err = subUnit.Remove() 270 c.Assert(err, jc.ErrorIsNil) 271 subUnit = nil 272 } 273 return err 274 } 275 expect := ".*: unit is not alive" 276 testWhenDying(c, unit, expect, expect, assignTest) 277 278 expect = ".*: machine is not alive" 279 unit, err = s.wordpress.AddUnit() 280 c.Assert(err, jc.ErrorIsNil) 281 testWhenDying(c, machine, expect, expect, assignTest) 282 } 283 284 func (s *AssignSuite) TestAssignMachineDifferentSeries(c *gc.C) { 285 machine, err := s.State.AddMachine("trusty", state.JobHostUnits) 286 c.Assert(err, jc.ErrorIsNil) 287 unit, err := s.wordpress.AddUnit() 288 c.Assert(err, jc.ErrorIsNil) 289 err = unit.AssignToMachine(machine) 290 c.Assert(err, gc.ErrorMatches, 291 `cannot assign unit "wordpress/0" to machine 0: series does not match`) 292 } 293 294 func (s *AssignSuite) TestPrincipals(c *gc.C) { 295 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 296 c.Assert(err, jc.ErrorIsNil) 297 principals := machine.Principals() 298 c.Assert(principals, jc.DeepEquals, []string{}) 299 300 unit, err := s.wordpress.AddUnit() 301 c.Assert(err, jc.ErrorIsNil) 302 err = unit.AssignToMachine(machine) 303 c.Assert(err, jc.ErrorIsNil) 304 305 err = machine.Refresh() 306 c.Assert(err, jc.ErrorIsNil) 307 principals = machine.Principals() 308 c.Assert(principals, jc.DeepEquals, []string{"wordpress/0"}) 309 } 310 311 func (s *AssignSuite) TestAssignMachinePrincipalsChange(c *gc.C) { 312 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 313 c.Assert(err, jc.ErrorIsNil) 314 unit, err := s.wordpress.AddUnit() 315 c.Assert(err, jc.ErrorIsNil) 316 err = unit.AssignToMachine(machine) 317 c.Assert(err, jc.ErrorIsNil) 318 unit, err = s.wordpress.AddUnit() 319 c.Assert(err, jc.ErrorIsNil) 320 err = unit.AssignToMachine(machine) 321 c.Assert(err, jc.ErrorIsNil) 322 subUnit := s.addSubordinate(c, unit) 323 324 checkPrincipals := func() []string { 325 err := machine.Refresh() 326 c.Assert(err, jc.ErrorIsNil) 327 return machine.Principals() 328 } 329 c.Assert(checkPrincipals(), gc.DeepEquals, []string{"wordpress/0", "wordpress/1"}) 330 331 err = subUnit.EnsureDead() 332 c.Assert(err, jc.ErrorIsNil) 333 err = subUnit.Remove() 334 c.Assert(err, jc.ErrorIsNil) 335 err = unit.EnsureDead() 336 c.Assert(err, jc.ErrorIsNil) 337 err = unit.Remove() 338 c.Assert(err, jc.ErrorIsNil) 339 c.Assert(checkPrincipals(), gc.DeepEquals, []string{"wordpress/0"}) 340 } 341 342 func (s *AssignSuite) assertAssignedUnit(c *gc.C, unit *state.Unit) string { 343 // Check the machine on the unit is set. 344 machineId, err := unit.AssignedMachineId() 345 c.Assert(err, jc.ErrorIsNil) 346 // Check that the principal is set on the machine. 347 machine, err := s.State.Machine(machineId) 348 c.Assert(err, jc.ErrorIsNil) 349 machineUnits, err := machine.Units() 350 c.Assert(err, jc.ErrorIsNil) 351 c.Assert(machineUnits, gc.HasLen, 1) 352 // Make sure it is the right unit. 353 c.Assert(machineUnits[0].Name(), gc.Equals, unit.Name()) 354 return machineId 355 } 356 357 func (s *AssignSuite) TestAssignUnitToNewMachine(c *gc.C) { 358 unit, err := s.wordpress.AddUnit() 359 c.Assert(err, jc.ErrorIsNil) 360 361 err = unit.AssignToNewMachine() 362 c.Assert(err, jc.ErrorIsNil) 363 s.assertAssignedUnit(c, unit) 364 } 365 366 func (s *AssignSuite) assertAssignUnitToNewMachineContainerConstraint(c *gc.C) { 367 unit, err := s.wordpress.AddUnit() 368 c.Assert(err, jc.ErrorIsNil) 369 err = unit.AssignToNewMachine() 370 c.Assert(err, jc.ErrorIsNil) 371 machineId := s.assertAssignedUnit(c, unit) 372 c.Assert(state.ParentId(machineId), gc.Not(gc.Equals), "") 373 c.Assert(state.ContainerTypeFromId(machineId), gc.Equals, instance.LXC) 374 } 375 376 func (s *AssignSuite) TestAssignUnitToNewMachineContainerConstraint(c *gc.C) { 377 // Set up service constraints. 378 scons := constraints.MustParse("container=lxc") 379 err := s.wordpress.SetConstraints(scons) 380 c.Assert(err, jc.ErrorIsNil) 381 s.assertAssignUnitToNewMachineContainerConstraint(c) 382 } 383 384 func (s *AssignSuite) TestAssignUnitToNewMachineDefaultContainerConstraint(c *gc.C) { 385 // Set up env constraints. 386 econs := constraints.MustParse("container=lxc") 387 err := s.State.SetModelConstraints(econs) 388 c.Assert(err, jc.ErrorIsNil) 389 s.assertAssignUnitToNewMachineContainerConstraint(c) 390 } 391 392 func (s *AssignSuite) TestAssignToNewMachineMakesDirty(c *gc.C) { 393 unit, err := s.wordpress.AddUnit() 394 c.Assert(err, jc.ErrorIsNil) 395 396 err = unit.AssignToNewMachine() 397 c.Assert(err, jc.ErrorIsNil) 398 mid, err := unit.AssignedMachineId() 399 c.Assert(err, jc.ErrorIsNil) 400 machine, err := s.State.Machine(mid) 401 c.Assert(err, jc.ErrorIsNil) 402 c.Assert(machine.Clean(), jc.IsFalse) 403 } 404 405 func (s *AssignSuite) TestAssignUnitToNewMachineSetsConstraints(c *gc.C) { 406 // Set up constraints. 407 scons := constraints.MustParse("mem=2G cpu-power=400") 408 err := s.wordpress.SetConstraints(scons) 409 c.Assert(err, jc.ErrorIsNil) 410 econs := constraints.MustParse("mem=4G cpu-cores=2") 411 err = s.State.SetModelConstraints(econs) 412 c.Assert(err, jc.ErrorIsNil) 413 414 // Unit will take combined service/environ constraints on creation. 415 unit, err := s.wordpress.AddUnit() 416 c.Assert(err, jc.ErrorIsNil) 417 418 // Change service/env constraints before assigning, to verify this. 419 scons = constraints.MustParse("mem=6G cpu-power=800") 420 err = s.wordpress.SetConstraints(scons) 421 c.Assert(err, jc.ErrorIsNil) 422 econs = constraints.MustParse("cpu-cores=4") 423 err = s.State.SetModelConstraints(econs) 424 c.Assert(err, jc.ErrorIsNil) 425 426 // The new machine takes the original combined unit constraints. 427 err = unit.AssignToNewMachine() 428 c.Assert(err, jc.ErrorIsNil) 429 err = unit.Refresh() 430 c.Assert(err, jc.ErrorIsNil) 431 mid, err := unit.AssignedMachineId() 432 c.Assert(err, jc.ErrorIsNil) 433 machine, err := s.State.Machine(mid) 434 c.Assert(err, jc.ErrorIsNil) 435 mcons, err := machine.Constraints() 436 c.Assert(err, jc.ErrorIsNil) 437 expect := constraints.MustParse("mem=2G cpu-cores=2 cpu-power=400") 438 c.Assert(mcons, gc.DeepEquals, expect) 439 } 440 441 func (s *AssignSuite) TestAssignUnitToNewMachineCleanAvailable(c *gc.C) { 442 unit, err := s.wordpress.AddUnit() 443 c.Assert(err, jc.ErrorIsNil) 444 445 // Add a clean machine. 446 clean, err := s.State.AddMachine("quantal", state.JobHostUnits) 447 c.Assert(err, jc.ErrorIsNil) 448 449 err = unit.AssignToNewMachine() 450 c.Assert(err, jc.ErrorIsNil) 451 // Check the machine on the unit is set. 452 machineId, err := unit.AssignedMachineId() 453 c.Assert(err, jc.ErrorIsNil) 454 // Check that the machine isn't our clean one. 455 machine, err := s.State.Machine(machineId) 456 c.Assert(err, jc.ErrorIsNil) 457 c.Assert(machine.Id(), gc.Not(gc.Equals), clean.Id()) 458 } 459 460 func (s *AssignSuite) TestAssignUnitToNewMachineAlreadyAssigned(c *gc.C) { 461 unit, err := s.wordpress.AddUnit() 462 c.Assert(err, jc.ErrorIsNil) 463 // Make the unit assigned 464 err = unit.AssignToNewMachine() 465 c.Assert(err, jc.ErrorIsNil) 466 // Try to assign it again 467 err = unit.AssignToNewMachine() 468 c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit is already assigned to a machine`) 469 } 470 471 func (s *AssignSuite) TestAssignUnitToNewMachineUnitNotAlive(c *gc.C) { 472 unit, err := s.wordpress.AddUnit() 473 c.Assert(err, jc.ErrorIsNil) 474 subUnit := s.addSubordinate(c, unit) 475 476 // Try to assign a dying unit... 477 err = unit.Destroy() 478 c.Assert(err, jc.ErrorIsNil) 479 err = unit.AssignToNewMachine() 480 c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit is not alive`) 481 482 // ...and a dead one. 483 err = subUnit.EnsureDead() 484 c.Assert(err, jc.ErrorIsNil) 485 err = subUnit.Remove() 486 c.Assert(err, jc.ErrorIsNil) 487 err = unit.EnsureDead() 488 c.Assert(err, jc.ErrorIsNil) 489 err = unit.AssignToNewMachine() 490 c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit is not alive`) 491 } 492 493 func (s *AssignSuite) TestAssignUnitToNewMachineUnitRemoved(c *gc.C) { 494 unit, err := s.wordpress.AddUnit() 495 c.Assert(err, jc.ErrorIsNil) 496 err = unit.Destroy() 497 c.Assert(err, jc.ErrorIsNil) 498 err = unit.AssignToNewMachine() 499 c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit not found`) 500 } 501 502 func (s *AssignSuite) TestAssignUnitToNewMachineBecomesDirty(c *gc.C) { 503 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 504 c.Assert(err, jc.ErrorIsNil) 505 506 // Set up constraints to specify we want to install into a container. 507 econs := constraints.MustParse("container=lxc") 508 err = s.State.SetModelConstraints(econs) 509 c.Assert(err, jc.ErrorIsNil) 510 511 // Create some units and a clean machine. 512 unit, err := s.wordpress.AddUnit() 513 c.Assert(err, jc.ErrorIsNil) 514 anotherUnit, err := s.wordpress.AddUnit() 515 c.Assert(err, jc.ErrorIsNil) 516 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 517 c.Assert(err, jc.ErrorIsNil) 518 519 makeDirty := txn.TestHook{ 520 Before: func() { c.Assert(unit.AssignToMachine(machine), gc.IsNil) }, 521 } 522 defer state.SetTestHooks(c, s.State, makeDirty).Check() 523 524 err = anotherUnit.AssignToNewMachineOrContainer() 525 c.Assert(err, jc.ErrorIsNil) 526 mid, err := unit.AssignedMachineId() 527 c.Assert(err, jc.ErrorIsNil) 528 c.Assert(mid, gc.Equals, "1") 529 530 mid, err = anotherUnit.AssignedMachineId() 531 c.Assert(err, jc.ErrorIsNil) 532 c.Assert(mid, gc.Equals, "2/lxc/0") 533 } 534 535 func (s *AssignSuite) TestAssignUnitToNewMachineBecomesHost(c *gc.C) { 536 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 537 c.Assert(err, jc.ErrorIsNil) 538 539 // Set up constraints to specify we want to install into a container. 540 econs := constraints.MustParse("container=lxc") 541 err = s.State.SetModelConstraints(econs) 542 c.Assert(err, jc.ErrorIsNil) 543 544 // Create a unit and a clean machine. 545 unit, err := s.wordpress.AddUnit() 546 c.Assert(err, jc.ErrorIsNil) 547 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 548 c.Assert(err, jc.ErrorIsNil) 549 550 addContainer := txn.TestHook{ 551 Before: func() { 552 _, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 553 Series: "quantal", 554 Jobs: []state.MachineJob{state.JobHostUnits}, 555 }, machine.Id(), instance.LXC) 556 c.Assert(err, jc.ErrorIsNil) 557 }, 558 } 559 defer state.SetTestHooks(c, s.State, addContainer).Check() 560 561 err = unit.AssignToNewMachineOrContainer() 562 c.Assert(err, jc.ErrorIsNil) 563 564 mid, err := unit.AssignedMachineId() 565 c.Assert(err, jc.ErrorIsNil) 566 c.Assert(mid, gc.Equals, "2/lxc/0") 567 } 568 569 func (s *AssignSuite) TestAssignUnitBadPolicy(c *gc.C) { 570 unit, err := s.wordpress.AddUnit() 571 c.Assert(err, jc.ErrorIsNil) 572 // Check nonsensical policy 573 err = s.State.AssignUnit(unit, state.AssignmentPolicy("random")) 574 c.Assert(err, gc.ErrorMatches, `.*unknown unit assignment policy: "random"`) 575 _, err = unit.AssignedMachineId() 576 c.Assert(err, gc.NotNil) 577 assertMachineCount(c, s.State, 0) 578 } 579 580 func (s *AssignSuite) TestAssignUnitLocalPolicy(c *gc.C) { 581 m, err := s.State.AddMachine("quantal", state.JobManageModel, state.JobHostUnits) // bootstrap machine 582 c.Assert(err, jc.ErrorIsNil) 583 unit, err := s.wordpress.AddUnit() 584 c.Assert(err, jc.ErrorIsNil) 585 586 for i := 0; i < 2; i++ { 587 err = s.State.AssignUnit(unit, state.AssignLocal) 588 c.Assert(err, jc.ErrorIsNil) 589 mid, err := unit.AssignedMachineId() 590 c.Assert(err, jc.ErrorIsNil) 591 c.Assert(mid, gc.Equals, m.Id()) 592 assertMachineCount(c, s.State, 1) 593 } 594 } 595 596 func (s *AssignSuite) assertAssignUnitNewPolicyNoContainer(c *gc.C) { 597 _, err := s.State.AddMachine("quantal", state.JobHostUnits) // available machine 598 c.Assert(err, jc.ErrorIsNil) 599 unit, err := s.wordpress.AddUnit() 600 c.Assert(err, jc.ErrorIsNil) 601 602 err = s.State.AssignUnit(unit, state.AssignNew) 603 c.Assert(err, jc.ErrorIsNil) 604 assertMachineCount(c, s.State, 2) 605 id, err := unit.AssignedMachineId() 606 c.Assert(err, jc.ErrorIsNil) 607 c.Assert(state.ParentId(id), gc.Equals, "") 608 } 609 610 func (s *AssignSuite) TestAssignUnitNewPolicy(c *gc.C) { 611 s.assertAssignUnitNewPolicyNoContainer(c) 612 } 613 614 func (s *AssignSuite) TestAssignUnitNewPolicyWithContainerConstraintIgnoresNone(c *gc.C) { 615 scons := constraints.MustParse("container=none") 616 err := s.wordpress.SetConstraints(scons) 617 c.Assert(err, jc.ErrorIsNil) 618 s.assertAssignUnitNewPolicyNoContainer(c) 619 } 620 621 func (s *AssignSuite) assertAssignUnitNewPolicyWithContainerConstraint(c *gc.C) { 622 unit, err := s.wordpress.AddUnit() 623 c.Assert(err, jc.ErrorIsNil) 624 err = s.State.AssignUnit(unit, state.AssignNew) 625 c.Assert(err, jc.ErrorIsNil) 626 assertMachineCount(c, s.State, 3) 627 id, err := unit.AssignedMachineId() 628 c.Assert(err, jc.ErrorIsNil) 629 c.Assert(id, gc.Equals, "1/lxc/0") 630 } 631 632 func (s *AssignSuite) TestAssignUnitNewPolicyWithContainerConstraint(c *gc.C) { 633 _, err := s.State.AddMachine("quantal", state.JobHostUnits) 634 c.Assert(err, jc.ErrorIsNil) 635 // Set up service constraints. 636 scons := constraints.MustParse("container=lxc") 637 err = s.wordpress.SetConstraints(scons) 638 c.Assert(err, jc.ErrorIsNil) 639 s.assertAssignUnitNewPolicyWithContainerConstraint(c) 640 } 641 642 func (s *AssignSuite) TestAssignUnitNewPolicyWithDefaultContainerConstraint(c *gc.C) { 643 _, err := s.State.AddMachine("quantal", state.JobHostUnits) 644 c.Assert(err, jc.ErrorIsNil) 645 // Set up env constraints. 646 econs := constraints.MustParse("container=lxc") 647 err = s.State.SetModelConstraints(econs) 648 c.Assert(err, jc.ErrorIsNil) 649 s.assertAssignUnitNewPolicyWithContainerConstraint(c) 650 } 651 652 func (s *AssignSuite) TestAssignUnitWithSubordinate(c *gc.C) { 653 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 654 c.Assert(err, jc.ErrorIsNil) 655 unit, err := s.wordpress.AddUnit() 656 c.Assert(err, jc.ErrorIsNil) 657 658 // Check cannot assign subordinates to machines 659 subUnit := s.addSubordinate(c, unit) 660 for _, policy := range []state.AssignmentPolicy{ 661 state.AssignLocal, state.AssignNew, state.AssignClean, state.AssignCleanEmpty, 662 } { 663 err = s.State.AssignUnit(subUnit, policy) 664 c.Assert(err, gc.ErrorMatches, `subordinate unit "logging/0" cannot be assigned directly to a machine`) 665 } 666 } 667 668 func assertMachineCount(c *gc.C, st *state.State, expect int) { 669 ms, err := st.AllMachines() 670 c.Assert(err, jc.ErrorIsNil) 671 c.Assert(ms, gc.HasLen, expect, gc.Commentf("%v", ms)) 672 } 673 674 // assignCleanSuite has tests for assigning units to 1. clean, and 2. clean&empty machines. 675 type assignCleanSuite struct { 676 ConnSuite 677 policy state.AssignmentPolicy 678 wordpress *state.Service 679 } 680 681 var _ = gc.Suite(&assignCleanSuite{ConnSuite{}, state.AssignCleanEmpty, nil}) 682 var _ = gc.Suite(&assignCleanSuite{ConnSuite{}, state.AssignClean, nil}) 683 684 func (s *assignCleanSuite) SetUpTest(c *gc.C) { 685 c.Logf("assignment policy for this test: %q", s.policy) 686 s.ConnSuite.SetUpTest(c) 687 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 688 s.wordpress = wordpress 689 pm := poolmanager.New(state.NewStateSettings(s.State)) 690 _, err := pm.Create("loop-pool", provider.LoopProviderType, map[string]interface{}{}) 691 c.Assert(err, jc.ErrorIsNil) 692 registry.RegisterEnvironStorageProviders("someprovider", provider.LoopProviderType) 693 } 694 695 func (s *assignCleanSuite) errorMessage(msg string) string { 696 context := "clean" 697 if s.policy == state.AssignCleanEmpty { 698 context += ", empty" 699 } 700 return fmt.Sprintf(msg, context) 701 } 702 703 func (s *assignCleanSuite) assignUnit(unit *state.Unit) (*state.Machine, error) { 704 if s.policy == state.AssignCleanEmpty { 705 return unit.AssignToCleanEmptyMachine() 706 } 707 return unit.AssignToCleanMachine() 708 } 709 710 func (s *assignCleanSuite) assertMachineEmpty(c *gc.C, machine *state.Machine) { 711 containers, err := machine.Containers() 712 c.Assert(err, jc.ErrorIsNil) 713 c.Assert(len(containers), gc.Equals, 0) 714 } 715 716 func (s *assignCleanSuite) assertMachineNotEmpty(c *gc.C, machine *state.Machine) { 717 containers, err := machine.Containers() 718 c.Assert(err, jc.ErrorIsNil) 719 c.Assert(len(containers), gc.Not(gc.Equals), 0) 720 } 721 722 // setupMachines creates a combination of machines with which to test. 723 func (s *assignCleanSuite) setupMachines(c *gc.C) (hostMachine *state.Machine, container *state.Machine, cleanEmptyMachine *state.Machine) { 724 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 725 c.Assert(err, jc.ErrorIsNil) 726 727 // Add some units to another service and allocate them to machines 728 service1 := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 729 units := make([]*state.Unit, 3) 730 for i := range units { 731 u, err := service1.AddUnit() 732 c.Assert(err, jc.ErrorIsNil) 733 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 734 c.Assert(err, jc.ErrorIsNil) 735 err = u.AssignToMachine(m) 736 c.Assert(err, jc.ErrorIsNil) 737 units[i] = u 738 } 739 740 // Create a new, clean machine but add containers so it is not empty. 741 hostMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) 742 c.Assert(err, jc.ErrorIsNil) 743 container, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ 744 Series: "quantal", 745 Jobs: []state.MachineJob{state.JobHostUnits}, 746 }, hostMachine.Id(), instance.LXC) 747 c.Assert(hostMachine.Clean(), jc.IsTrue) 748 s.assertMachineNotEmpty(c, hostMachine) 749 750 // Create a new, clean, empty machine. 751 cleanEmptyMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) 752 c.Assert(err, jc.ErrorIsNil) 753 c.Assert(cleanEmptyMachine.Clean(), jc.IsTrue) 754 s.assertMachineEmpty(c, cleanEmptyMachine) 755 return hostMachine, container, cleanEmptyMachine 756 } 757 758 func (s *assignCleanSuite) assertAssignUnit(c *gc.C, expectedMachine *state.Machine) { 759 unit, err := s.wordpress.AddUnit() 760 c.Assert(err, jc.ErrorIsNil) 761 reusedMachine, err := s.assignUnit(unit) 762 c.Assert(err, jc.ErrorIsNil) 763 c.Assert(reusedMachine.Id(), gc.Equals, expectedMachine.Id()) 764 c.Assert(reusedMachine.Clean(), jc.IsFalse) 765 } 766 767 func (s *assignCleanSuite) TestAssignUnit(c *gc.C) { 768 hostMachine, container, cleanEmptyMachine := s.setupMachines(c) 769 // Check that AssignToClean(Empty)Machine finds a newly created, clean (maybe empty) machine. 770 if s.policy == state.AssignCleanEmpty { 771 // The first clean, empty machine is the container. 772 s.assertAssignUnit(c, container) 773 // The next deployment will use the remaining clean, empty machine. 774 s.assertAssignUnit(c, cleanEmptyMachine) 775 } else { 776 s.assertAssignUnit(c, hostMachine) 777 } 778 } 779 780 func (s *assignCleanSuite) TestAssignUnitTwiceFails(c *gc.C) { 781 s.setupMachines(c) 782 unit, err := s.wordpress.AddUnit() 783 c.Assert(err, jc.ErrorIsNil) 784 // Assign the first time. 785 _, err = s.assignUnit(unit) 786 c.Assert(err, jc.ErrorIsNil) 787 788 // Check that it fails when called again, even when there's an available machine 789 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 790 c.Assert(err, jc.ErrorIsNil) 791 _, err = s.assignUnit(unit) 792 c.Assert(err, gc.ErrorMatches, s.errorMessage(`cannot assign unit "wordpress/0" to %s machine: unit is already assigned to a machine`)) 793 c.Assert(m.EnsureDead(), gc.IsNil) 794 c.Assert(m.Remove(), gc.IsNil) 795 } 796 797 const eligibleMachinesInUse = "all eligible machines in use" 798 799 func (s *assignCleanSuite) TestAssignToMachineNoneAvailable(c *gc.C) { 800 // Try to assign a unit to a clean (maybe empty) machine and check that we can't. 801 unit, err := s.wordpress.AddUnit() 802 c.Assert(err, jc.ErrorIsNil) 803 804 m, err := s.assignUnit(unit) 805 c.Assert(m, gc.IsNil) 806 c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) 807 808 // Add a state management machine which can host units and check it is not chosen. 809 // Note that this must the first machine added, as AddMachine can only 810 // be used to add state-manager machines for the bootstrap machine. 811 m, err = s.State.AddMachine("quantal", state.JobManageModel, state.JobHostUnits) 812 c.Assert(err, jc.ErrorIsNil) 813 m, err = s.assignUnit(unit) 814 c.Assert(m, gc.IsNil) 815 c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) 816 817 // Add a dying machine and check that it is not chosen. 818 m, err = s.State.AddMachine("quantal", state.JobHostUnits) 819 c.Assert(err, jc.ErrorIsNil) 820 err = m.Destroy() 821 c.Assert(err, jc.ErrorIsNil) 822 m, err = s.assignUnit(unit) 823 c.Assert(m, gc.IsNil) 824 c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) 825 826 // Add two environ manager machines and check they are not chosen. 827 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 828 c.Assert(err, jc.ErrorIsNil) 829 c.Assert(changes.Added, gc.HasLen, 3) 830 831 m, err = s.assignUnit(unit) 832 c.Assert(m, gc.IsNil) 833 c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) 834 835 // Add a machine with the wrong series and check it is not chosen. 836 m, err = s.State.AddMachine("anotherseries", state.JobHostUnits) 837 c.Assert(err, jc.ErrorIsNil) 838 m, err = s.assignUnit(unit) 839 c.Assert(m, gc.IsNil) 840 c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) 841 } 842 843 var assignUsingConstraintsTests = []struct { 844 unitConstraints string 845 hardwareCharacteristics string 846 assignOk bool 847 }{ 848 { 849 unitConstraints: "", 850 hardwareCharacteristics: "", 851 assignOk: true, 852 }, { 853 unitConstraints: "arch=amd64", 854 hardwareCharacteristics: "none", 855 assignOk: false, 856 }, { 857 unitConstraints: "arch=amd64", 858 hardwareCharacteristics: "cpu-cores=1", 859 assignOk: false, 860 }, { 861 unitConstraints: "arch=amd64", 862 hardwareCharacteristics: "arch=amd64", 863 assignOk: true, 864 }, { 865 unitConstraints: "arch=amd64", 866 hardwareCharacteristics: "arch=i386", 867 assignOk: false, 868 }, { 869 unitConstraints: "mem=4G", 870 hardwareCharacteristics: "none", 871 assignOk: false, 872 }, { 873 unitConstraints: "mem=4G", 874 hardwareCharacteristics: "cpu-cores=1", 875 assignOk: false, 876 }, { 877 unitConstraints: "mem=4G", 878 hardwareCharacteristics: "mem=4G", 879 assignOk: true, 880 }, { 881 unitConstraints: "mem=4G", 882 hardwareCharacteristics: "mem=2G", 883 assignOk: false, 884 }, { 885 unitConstraints: "cpu-cores=2", 886 hardwareCharacteristics: "cpu-cores=2", 887 assignOk: true, 888 }, { 889 unitConstraints: "cpu-cores=2", 890 hardwareCharacteristics: "cpu-cores=1", 891 assignOk: false, 892 }, { 893 unitConstraints: "cpu-cores=2", 894 hardwareCharacteristics: "mem=4G", 895 assignOk: false, 896 }, { 897 unitConstraints: "cpu-power=50", 898 hardwareCharacteristics: "cpu-power=50", 899 assignOk: true, 900 }, { 901 unitConstraints: "cpu-power=100", 902 hardwareCharacteristics: "cpu-power=50", 903 assignOk: false, 904 }, { 905 unitConstraints: "cpu-power=50", 906 hardwareCharacteristics: "mem=4G", 907 assignOk: false, 908 }, { 909 unitConstraints: "root-disk=8192", 910 hardwareCharacteristics: "cpu-power=50", 911 assignOk: false, 912 }, { 913 unitConstraints: "root-disk=8192", 914 hardwareCharacteristics: "root-disk=4096", 915 assignOk: false, 916 }, { 917 unitConstraints: "root-disk=8192", 918 hardwareCharacteristics: "root-disk=8192", 919 assignOk: true, 920 }, { 921 unitConstraints: "arch=amd64 mem=4G cpu-cores=2 root-disk=8192", 922 hardwareCharacteristics: "arch=amd64 mem=8G cpu-cores=2 root-disk=8192 cpu-power=50", 923 assignOk: true, 924 }, { 925 unitConstraints: "arch=amd64 mem=4G cpu-cores=2 root-disk=8192", 926 hardwareCharacteristics: "arch=amd64 mem=8G cpu-cores=1 root-disk=4096 cpu-power=50", 927 assignOk: false, 928 }, 929 } 930 931 func (s *assignCleanSuite) TestAssignUsingConstraintsToMachine(c *gc.C) { 932 for i, t := range assignUsingConstraintsTests { 933 c.Logf("test %d", i) 934 cons := constraints.MustParse(t.unitConstraints) 935 err := s.State.SetModelConstraints(cons) 936 c.Assert(err, jc.ErrorIsNil) 937 938 unit, err := s.wordpress.AddUnit() 939 c.Assert(err, jc.ErrorIsNil) 940 941 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 942 c.Assert(err, jc.ErrorIsNil) 943 if t.hardwareCharacteristics != "none" { 944 hc := instance.MustParseHardware(t.hardwareCharacteristics) 945 err = m.SetProvisioned("inst-id", "fake_nonce", &hc) 946 c.Assert(err, jc.ErrorIsNil) 947 } 948 949 um, err := s.assignUnit(unit) 950 if t.assignOk { 951 c.Assert(err, jc.ErrorIsNil) 952 c.Assert(um.Id(), gc.Equals, m.Id()) 953 } else { 954 c.Assert(um, gc.IsNil) 955 c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) 956 // Destroy the machine so it can't be used for the next test. 957 err = m.Destroy() 958 c.Assert(err, jc.ErrorIsNil) 959 } 960 } 961 } 962 963 func (s *assignCleanSuite) TestAssignUnitWithRemovedService(c *gc.C) { 964 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 965 c.Assert(err, jc.ErrorIsNil) 966 unit, err := s.wordpress.AddUnit() 967 c.Assert(err, jc.ErrorIsNil) 968 969 // Fail if service is removed. 970 removeAllUnits(c, s.wordpress) 971 err = s.wordpress.Destroy() 972 c.Assert(err, jc.ErrorIsNil) 973 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 974 c.Assert(err, jc.ErrorIsNil) 975 _, err = s.assignUnit(unit) 976 c.Assert(err, gc.ErrorMatches, s.errorMessage(`cannot assign unit "wordpress/0" to %s machine.* not found`)) 977 } 978 979 func (s *assignCleanSuite) TestAssignUnitToMachineWithRemovedUnit(c *gc.C) { 980 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 981 c.Assert(err, jc.ErrorIsNil) 982 unit, err := s.wordpress.AddUnit() 983 c.Assert(err, jc.ErrorIsNil) 984 // Fail if unit is removed. 985 err = unit.EnsureDead() 986 c.Assert(err, jc.ErrorIsNil) 987 err = unit.Remove() 988 c.Assert(err, jc.ErrorIsNil) 989 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 990 c.Assert(err, jc.ErrorIsNil) 991 992 _, err = s.assignUnit(unit) 993 c.Assert(err, gc.ErrorMatches, s.errorMessage(`cannot assign unit "wordpress/0" to %s machine.*: unit not found`)) 994 } 995 996 func (s *assignCleanSuite) TestAssignUnitToMachineWorksWithMachine0(c *gc.C) { 997 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 998 c.Assert(err, jc.ErrorIsNil) 999 c.Assert(m.Id(), gc.Equals, "0") 1000 unit, err := s.wordpress.AddUnit() 1001 c.Assert(err, jc.ErrorIsNil) 1002 assignedTo, err := s.assignUnit(unit) 1003 c.Assert(err, jc.ErrorIsNil) 1004 c.Assert(assignedTo.Id(), gc.Equals, "0") 1005 } 1006 1007 func (s *assignCleanSuite) setupSingleStorage(c *gc.C, kind, pool string) (*state.Service, *state.Unit, names.StorageTag) { 1008 // There are test charms called "storage-block" and 1009 // "storage-filesystem" which are what you'd expect. 1010 ch := s.AddTestingCharm(c, "storage-"+kind) 1011 storage := map[string]state.StorageConstraints{ 1012 "data": makeStorageCons(pool, 1024, 1), 1013 } 1014 service := s.AddTestingServiceWithStorage(c, "storage-"+kind, ch, storage) 1015 unit, err := service.AddUnit() 1016 c.Assert(err, jc.ErrorIsNil) 1017 storageTag := names.NewStorageTag("data/0") 1018 return service, unit, storageTag 1019 } 1020 1021 func (s *assignCleanSuite) TestAssignToMachine(c *gc.C) { 1022 _, unit, _ := s.setupSingleStorage(c, "filesystem", "loop-pool") 1023 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1024 c.Assert(err, jc.ErrorIsNil) 1025 err = unit.AssignToMachine(machine) 1026 c.Assert(err, jc.ErrorIsNil) 1027 filesystemAttachments, err := s.State.MachineFilesystemAttachments(machine.MachineTag()) 1028 c.Assert(err, jc.ErrorIsNil) 1029 c.Assert(filesystemAttachments, gc.HasLen, 1) 1030 } 1031 1032 func (s *assignCleanSuite) TestAssignToMachineErrors(c *gc.C) { 1033 registry.RegisterProvider("static", &dummy.StorageProvider{ 1034 IsDynamic: false, 1035 }) 1036 registry.RegisterEnvironStorageProviders("someprovider", "static") 1037 defer registry.RegisterProvider("static", nil) 1038 1039 _, unit, _ := s.setupSingleStorage(c, "filesystem", "static") 1040 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1041 c.Assert(err, jc.ErrorIsNil) 1042 err = unit.AssignToMachine(machine) 1043 c.Assert( 1044 err, gc.ErrorMatches, 1045 `cannot assign unit "storage-filesystem/0" to machine 0: "static" storage provider does not support dynamic storage`, 1046 ) 1047 1048 container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 1049 Series: "quantal", 1050 Jobs: []state.MachineJob{state.JobHostUnits}, 1051 }, machine.Id(), instance.LXC) 1052 c.Assert(err, jc.ErrorIsNil) 1053 err = unit.AssignToMachine(container) 1054 c.Assert(err, gc.ErrorMatches, `cannot assign unit "storage-filesystem/0" to machine 0/lxc/0: adding storage to lxc container not supported`) 1055 } 1056 1057 func (s *assignCleanSuite) TestAssignUnitWithNonDynamicStorageCleanAvailable(c *gc.C) { 1058 registry.RegisterProvider("static", &dummy.StorageProvider{ 1059 IsDynamic: false, 1060 }) 1061 registry.RegisterEnvironStorageProviders("someprovider", "static") 1062 defer registry.RegisterProvider("static", nil) 1063 _, unit, _ := s.setupSingleStorage(c, "filesystem", "static") 1064 storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag()) 1065 c.Assert(err, jc.ErrorIsNil) 1066 c.Assert(storageAttachments, gc.HasLen, 1) 1067 1068 // Add a clean machine. 1069 clean, err := s.State.AddMachine("quantal", state.JobHostUnits) 1070 c.Assert(err, jc.ErrorIsNil) 1071 1072 // assign the unit to a machine, requesting clean/empty. Since 1073 // the unit has non dynamic storage instances associated, 1074 // it will be forced onto a new machine. 1075 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 1076 c.Assert(err, jc.ErrorIsNil) 1077 1078 // Check the machine on the unit is set. 1079 machineId, err := unit.AssignedMachineId() 1080 c.Assert(err, jc.ErrorIsNil) 1081 // Check that the machine isn't our clean one. 1082 c.Assert(machineId, gc.Not(gc.Equals), clean.Id()) 1083 } 1084 1085 func (s *assignCleanSuite) TestAssignUnitWithDynamicStorageCleanAvailable(c *gc.C) { 1086 _, unit, _ := s.setupSingleStorage(c, "filesystem", "loop-pool") 1087 storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag()) 1088 c.Assert(err, jc.ErrorIsNil) 1089 c.Assert(storageAttachments, gc.HasLen, 1) 1090 1091 // Add a clean machine. 1092 clean, err := s.State.AddMachine("quantal", state.JobHostUnits) 1093 c.Assert(err, jc.ErrorIsNil) 1094 1095 // assign the unit to a machine, requesting clean/empty 1096 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 1097 c.Assert(err, jc.ErrorIsNil) 1098 1099 // Check the machine on the unit is set. 1100 machineId, err := unit.AssignedMachineId() 1101 c.Assert(err, jc.ErrorIsNil) 1102 // Check that the machine isn't our clean one. 1103 c.Assert(machineId, gc.Equals, clean.Id()) 1104 1105 // Check that a volume attachments were added to the machine. 1106 machine, err := s.State.Machine(machineId) 1107 c.Assert(err, jc.ErrorIsNil) 1108 volumeAttachments, err := s.State.MachineVolumeAttachments(machine.MachineTag()) 1109 c.Assert(err, jc.ErrorIsNil) 1110 c.Assert(volumeAttachments, gc.HasLen, 1) 1111 1112 volume, err := s.State.Volume(volumeAttachments[0].Volume()) 1113 c.Assert(err, jc.ErrorIsNil) 1114 volumeStorageInstance, err := volume.StorageInstance() 1115 c.Assert(err, jc.ErrorIsNil) 1116 c.Assert(volumeStorageInstance, gc.Equals, storageAttachments[0].StorageInstance()) 1117 } 1118 1119 func (s *assignCleanSuite) TestAssignUnitPolicy(c *gc.C) { 1120 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 1121 c.Assert(err, jc.ErrorIsNil) 1122 1123 // Check unassigned placements with no clean and/or empty machines. 1124 for i := 0; i < 10; i++ { 1125 unit, err := s.wordpress.AddUnit() 1126 c.Assert(err, jc.ErrorIsNil) 1127 err = s.State.AssignUnit(unit, s.policy) 1128 c.Assert(err, jc.ErrorIsNil) 1129 mid, err := unit.AssignedMachineId() 1130 c.Assert(err, jc.ErrorIsNil) 1131 c.Assert(mid, gc.Equals, strconv.Itoa(1+i)) 1132 assertMachineCount(c, s.State, i+2) 1133 1134 // Sanity check that the machine knows about its assigned unit and was 1135 // created with the appropriate series. 1136 m, err := s.State.Machine(mid) 1137 c.Assert(err, jc.ErrorIsNil) 1138 units, err := m.Units() 1139 c.Assert(err, jc.ErrorIsNil) 1140 c.Assert(units, gc.HasLen, 1) 1141 c.Assert(units[0].Name(), gc.Equals, unit.Name()) 1142 c.Assert(m.Series(), gc.Equals, "quantal") 1143 } 1144 1145 // Remove units from alternate machines. These machines will still be 1146 // considered as dirty so will continue to be ignored by the policy. 1147 for i := 1; i < 11; i += 2 { 1148 mid := strconv.Itoa(i) 1149 m, err := s.State.Machine(mid) 1150 c.Assert(err, jc.ErrorIsNil) 1151 units, err := m.Units() 1152 c.Assert(err, jc.ErrorIsNil) 1153 c.Assert(units, gc.HasLen, 1) 1154 unit := units[0] 1155 err = unit.UnassignFromMachine() 1156 c.Assert(err, jc.ErrorIsNil) 1157 err = unit.Destroy() 1158 c.Assert(err, jc.ErrorIsNil) 1159 } 1160 1161 var expectedMachines []string 1162 // Create a new, clean machine but add containers so it is not empty. 1163 hostMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1164 c.Assert(err, jc.ErrorIsNil) 1165 container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 1166 Series: "quantal", 1167 Jobs: []state.MachineJob{state.JobHostUnits}, 1168 }, hostMachine.Id(), instance.LXC) 1169 c.Assert(hostMachine.Clean(), jc.IsTrue) 1170 s.assertMachineNotEmpty(c, hostMachine) 1171 if s.policy == state.AssignClean { 1172 expectedMachines = append(expectedMachines, hostMachine.Id()) 1173 } 1174 expectedMachines = append(expectedMachines, container.Id()) 1175 1176 // Add some more clean machines 1177 for i := 0; i < 4; i++ { 1178 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 1179 c.Assert(err, jc.ErrorIsNil) 1180 expectedMachines = append(expectedMachines, m.Id()) 1181 } 1182 1183 // Assign units to all the expectedMachines machines. 1184 var got []string 1185 for _ = range expectedMachines { 1186 unit, err := s.wordpress.AddUnit() 1187 c.Assert(err, jc.ErrorIsNil) 1188 err = s.State.AssignUnit(unit, s.policy) 1189 c.Assert(err, jc.ErrorIsNil) 1190 mid, err := unit.AssignedMachineId() 1191 c.Assert(err, jc.ErrorIsNil) 1192 got = append(got, mid) 1193 } 1194 sort.Strings(expectedMachines) 1195 sort.Strings(got) 1196 c.Assert(got, gc.DeepEquals, expectedMachines) 1197 } 1198 1199 func (s *assignCleanSuite) TestAssignUnitPolicyWithContainers(c *gc.C) { 1200 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 1201 c.Assert(err, jc.ErrorIsNil) 1202 1203 // Create a machine and add a new container. 1204 hostMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1205 c.Assert(err, jc.ErrorIsNil) 1206 container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 1207 Series: "quantal", 1208 Jobs: []state.MachineJob{state.JobHostUnits}, 1209 }, hostMachine.Id(), instance.LXC) 1210 err = hostMachine.Refresh() 1211 c.Assert(err, jc.ErrorIsNil) 1212 c.Assert(hostMachine.Clean(), jc.IsTrue) 1213 s.assertMachineNotEmpty(c, hostMachine) 1214 1215 // Set up constraints to specify we want to install into a container. 1216 econs := constraints.MustParse("container=lxc") 1217 err = s.State.SetModelConstraints(econs) 1218 c.Assert(err, jc.ErrorIsNil) 1219 1220 // Check the first placement goes into the newly created, clean container above. 1221 unit, err := s.wordpress.AddUnit() 1222 c.Assert(err, jc.ErrorIsNil) 1223 err = s.State.AssignUnit(unit, s.policy) 1224 c.Assert(err, jc.ErrorIsNil) 1225 mid, err := unit.AssignedMachineId() 1226 c.Assert(err, jc.ErrorIsNil) 1227 c.Assert(mid, gc.Equals, container.Id()) 1228 1229 assertContainerPlacement := func(expectedNumUnits int) { 1230 unit, err := s.wordpress.AddUnit() 1231 c.Assert(err, jc.ErrorIsNil) 1232 err = s.State.AssignUnit(unit, s.policy) 1233 c.Assert(err, jc.ErrorIsNil) 1234 mid, err := unit.AssignedMachineId() 1235 c.Assert(err, jc.ErrorIsNil) 1236 c.Assert(mid, gc.Equals, fmt.Sprintf("%d/lxc/0", expectedNumUnits+1)) 1237 assertMachineCount(c, s.State, 2*expectedNumUnits+3) 1238 1239 // Sanity check that the machine knows about its assigned unit and was 1240 // created with the appropriate series. 1241 m, err := s.State.Machine(mid) 1242 c.Assert(err, jc.ErrorIsNil) 1243 units, err := m.Units() 1244 c.Assert(err, jc.ErrorIsNil) 1245 c.Assert(units, gc.HasLen, 1) 1246 c.Assert(units[0].Name(), gc.Equals, unit.Name()) 1247 c.Assert(m.Series(), gc.Equals, "quantal") 1248 } 1249 1250 // Check unassigned placements with no clean and/or empty machines cause a new container to be created. 1251 assertContainerPlacement(1) 1252 assertContainerPlacement(2) 1253 1254 // Create a new, clean instance and check that the next container creation uses it. 1255 hostMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) 1256 c.Assert(err, jc.ErrorIsNil) 1257 unit, err = s.wordpress.AddUnit() 1258 c.Assert(err, jc.ErrorIsNil) 1259 err = s.State.AssignUnit(unit, s.policy) 1260 c.Assert(err, jc.ErrorIsNil) 1261 mid, err = unit.AssignedMachineId() 1262 c.Assert(err, jc.ErrorIsNil) 1263 c.Assert(mid, gc.Equals, hostMachine.Id()+"/lxc/0") 1264 } 1265 1266 func (s *assignCleanSuite) TestAssignUnitPolicyConcurrently(c *gc.C) { 1267 _, err := s.State.AddMachine("quantal", state.JobManageModel) // bootstrap machine 1268 c.Assert(err, jc.ErrorIsNil) 1269 us := make([]*state.Unit, 50) 1270 for i := range us { 1271 us[i], err = s.wordpress.AddUnit() 1272 c.Assert(err, jc.ErrorIsNil) 1273 } 1274 type result struct { 1275 u *state.Unit 1276 err error 1277 } 1278 done := make(chan result) 1279 for i, u := range us { 1280 i, u := i, u 1281 go func() { 1282 // Start the AssignUnit at different times 1283 // to increase the likeliness of a race. 1284 time.Sleep(time.Duration(i) * time.Millisecond / 2) 1285 err := s.State.AssignUnit(u, s.policy) 1286 done <- result{u, err} 1287 }() 1288 } 1289 assignments := make(map[string][]*state.Unit) 1290 for _ = range us { 1291 r := <-done 1292 if !c.Check(r.err, gc.IsNil) { 1293 continue 1294 } 1295 id, err := r.u.AssignedMachineId() 1296 c.Assert(err, jc.ErrorIsNil) 1297 assignments[id] = append(assignments[id], r.u) 1298 } 1299 for id, us := range assignments { 1300 if len(us) != 1 { 1301 c.Errorf("machine %s expected one unit, got %q", id, us) 1302 } 1303 } 1304 c.Assert(assignments, gc.HasLen, len(us)) 1305 }