github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/assign_test.go (about)

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