github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/assign_test.go (about)

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