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