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